Claude güncellemesi Workflow Reset güncellemesi

This commit is contained in:
Sedat ÖZTÜRK 2026-08-11 14:43:51 +03:00
parent db24baed91
commit 1fe4abaeb6
13 changed files with 304 additions and 2 deletions

View file

@ -9,6 +9,7 @@ public class WorkflowDto
public string ApprovalStatusFieldName { get; set; }
public string ApprovalDescriptionFieldName { get; set; }
public bool ApprovalIsFilterUserName { get; set; }
public bool ApprovalIsResetWorkflow { get; set; }
public List<ListFormWorkflowCriteriaDto> Criteria { get; set; } = [];
}

View file

@ -11,5 +11,6 @@ public interface IListFormWorkflowAppService : IApplicationService
Task DeleteCriteriaAsync(string id);
Task<ListFormWorkflowStateDto> ResetDemoAsync(string listFormCode = null);
Task<WorkflowRunResultDto> StartAsync(StartWorkflowDto input);
Task<WorkflowRunResultDto> ResetAsync(StartWorkflowDto input);
Task<WorkflowRunResultDto> DecisionAsync(DecisionWorkflowDto input);
}

View file

@ -318,6 +318,65 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
return result;
}
[HttpPost("reset")]
public async Task<WorkflowRunResultDto> ResetAsync(StartWorkflowDto input)
{
if (input.Keys?.Length > 1)
{
return await RunForEachKeyAsync(input.Keys, keys => ResetSingleAsync(input.ListFormCode, keys));
}
return await ResetSingleAsync(input.ListFormCode, input.Keys);
}
private async Task<WorkflowRunResultDto> ResetSingleAsync(string listFormCode, object[] keys)
{
var context = await CreateRunContextAsync(listFormCode, keys);
if (!context.Workflow.ApprovalIsResetWorkflow)
{
throw new UserFriendlyException("Bu liste formu için workflow sıfırlama tanımlı değil.");
}
var start = context.Criteria.FirstOrDefault(x => x.Kind == "Start")
?? throw new UserFriendlyException("Workflow başlangıç adımı bulunamadı.");
var previousStatus = GetRowValue(context.Row, context.Workflow.ApprovalStatusFieldName)?.ToString();
var clear = new Dictionary<string, object>
{
[context.Workflow.ApprovalStatusFieldName] = null
};
if (!context.Workflow.ApprovalUserFieldName.IsNullOrWhiteSpace())
{
clear[context.Workflow.ApprovalUserFieldName] = null;
}
if (!context.Workflow.ApprovalDateFieldName.IsNullOrWhiteSpace())
{
clear[context.Workflow.ApprovalDateFieldName] = null;
}
if (!context.Workflow.ApprovalDescriptionFieldName.IsNullOrWhiteSpace())
{
clear[context.Workflow.ApprovalDescriptionFieldName] = null;
}
await UpdateRowAsync(context, clear);
MergeRowValues(context.Row, clear);
context.WorkflowNoteRows.Add(("Reset By: ", ResolveCurrentUserDisplayName()));
if (!previousStatus.IsNullOrWhiteSpace())
{
context.WorkflowNoteRows.Add(("Reset Step: ", previousStatus));
}
var result = await RunUntilWaitAsync(context, start);
await InsertWorkflowNoteAsync(
context,
$"Workflow Reset: {start.Title}",
BuildWorkflowNoteContent(context.WorkflowNoteRows));
return result;
}
[HttpPost("decision")]
public async Task<WorkflowRunResultDto> DecisionAsync(DecisionWorkflowDto input)
{

View file

@ -3792,6 +3792,24 @@
"en": "Workflow has already been started for the selected record",
"tr": "Seçili kayıt icin workflow zaten başlamış."
},
{
"resourceName": "Platform",
"key": "ListForms.ListForm.WorkflowReset",
"en": "Workflow Reset",
"tr": "İş Akışı Sıfırla"
},
{
"resourceName": "Platform",
"key": "ListForms.ListForm.WorkflowResetMessage",
"en": "All approvals of the selected {0} record(s) will be removed and the workflow will start over. Do you confirm?",
"tr": "Seçili {0} kaydın tüm onayları kaldırılacak ve iş akışı baştan başlatılacak. Onaylıyor musunuz?"
},
{
"resourceName": "Platform",
"key": "ListForms.ListForm.WorkflowNotStarted",
"en": "Workflow has not been started for the selected record",
"tr": "Seçili kayıt için workflow başlatılmamış."
},
{
"resourceName": "Platform",
"key": "SeciliKayitlarSilmekIstiyormusunuz",
@ -20034,6 +20052,12 @@
"en": "Filter User Name?",
"tr": "Kullanıcı Adı Filtresin mi?"
},
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.Workflow.ApprovalIsResetWorkflow",
"en": "Workflow Reset?",
"tr": "İş Akışı Sıfırlama?"
},
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.Workflow.ApprovalDescriptionFieldName",

View file

@ -10,6 +10,7 @@ public class Workflow : ValueObject
public string ApprovalStatusFieldName { get; set; }
public string ApprovalDescriptionFieldName { get; set; }
public bool ApprovalIsFilterUserName { get; set; }
public bool ApprovalIsResetWorkflow { get; set; }
protected override IEnumerable<object> GetAtomicValues()
{
@ -18,6 +19,7 @@ public class Workflow : ValueObject
yield return ApprovalStatusFieldName;
yield return ApprovalDescriptionFieldName;
yield return ApprovalIsFilterUserName;
yield return ApprovalIsResetWorkflow;
}
}

View file

@ -930,6 +930,7 @@ export interface WorkflowDto {
approvalStatusFieldName: string
approvalDescriptionFieldName: string
approvalIsFilterUserName: boolean
approvalIsResetWorkflow: boolean
criteria: ListFormWorkflowCriteriaDto[]
}

View file

@ -101,6 +101,16 @@ export const workflowService = {
return response.data
},
async resetWorkflow(listFormCode: string, keys: unknown[]) {
const response = await apiService.fetchData<WorkflowRunResultDto>({
method: 'POST',
url: `${baseUrl}/reset`,
data: { listFormCode, keys },
})
return response.data
},
async decideWorkflow(
listFormCode: string,
keys: unknown[],

View file

@ -318,6 +318,7 @@ export function FormTabWorkflow(
approvalDateFieldName: string(),
approvalDescriptionFieldName: string(),
approvalIsFilterUserName: bool(),
approvalIsResetWorkflow: bool(),
}),
})
@ -340,7 +341,7 @@ export function FormTabWorkflow(
<Form>
<FormContainer size="sm">
<Card className="my-2">
<div className="grid grid-cols-1 md:grid-cols-5 gap-2">
<div className="grid grid-cols-1 md:grid-cols-6 gap-2">
<FormItem
asterisk
label={translate('::ListForms.ListFormEdit.Workflow.ApprovalUserFieldName')}
@ -454,6 +455,19 @@ export function FormTabWorkflow(
>
<Field name="workflowDto.approvalIsFilterUserName" component={Checkbox} />
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.Workflow.ApprovalIsResetWorkflow')}
invalid={
!!(
errors.workflowDto?.approvalIsResetWorkflow &&
touched.workflowDto?.approvalIsResetWorkflow
)
}
errorMessage={errors.workflowDto?.approvalIsResetWorkflow as string}
>
<Field name="workflowDto.approvalIsResetWorkflow" component={Checkbox} />
</FormItem>
</div>
<Button block size="sm" variant="solid" type="submit" loading={isSubmitting}>

View file

@ -140,6 +140,7 @@ const initialValues: ListFormWizardDto = {
approvalStatusFieldName: '',
approvalDescriptionFieldName: '',
approvalIsFilterUserName: false,
approvalIsResetWorkflow: false,
criteria: [],
},
languageTextMenuEn: '',
@ -369,6 +370,7 @@ const Wizard = () => {
approvalStatusFieldName: '',
approvalDescriptionFieldName: '',
approvalIsFilterUserName: false,
approvalIsResetWorkflow: false,
criteria: [],
})
const [criteria, setCriteria] = useState<ListFormWorkflowCriteriaDto[]>([])

View file

@ -558,6 +558,10 @@ const WizardStep11 = ({
label="Is Filter User Name?"
value={workflow.approvalIsFilterUserName === true ? 'Yes' : 'No'}
/>
<Row
label="Is Reset Workflow?"
value={workflow.approvalIsResetWorkflow === true ? 'Yes' : 'No'}
/>
</div>
{workflowItems.length > 0 && (
<div className="flex flex-col gap-2">

View file

@ -300,7 +300,7 @@ function WizardStep6({
<Form>
<Card className="mb-4" header={translate('::ListForms.ListFormEdit.TabWorkflow')}>
<FormContainer>
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
<div className="grid grid-cols-1 md:grid-cols-6 gap-4">
{[
[
'approvalUserFieldName',
@ -381,6 +381,26 @@ function WizardStep6({
)}
</Field>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.Workflow.ApprovalIsResetWorkflow')}
>
<Field name="approvalIsResetWorkflow">
{({ field, form }: FieldProps<boolean>) => (
<Checkbox
name={field.name}
checked={Boolean(values.approvalIsResetWorkflow)}
onChange={(checked) => {
form.setFieldValue(field.name, checked)
onWorkflowChange({
...values,
approvalIsResetWorkflow: checked,
criteria,
})
}}
/>
)}
</Field>
</FormItem>
</div>
</FormContainer>
</Card>

View file

@ -5,6 +5,7 @@ const orderedToolbarItemNames = [
'addRowButton',
'refreshButton',
'openDynamicGridReport',
'workflowReset',
'workflowStart',
'expandAllButton',
'collapseAllButton',

View file

@ -154,6 +154,85 @@ const useToolbar = ({
approvalCriteria.length > 0 &&
grdOpt.updateServiceAddress
) {
if (workflowOptions.approvalIsResetWorkflow) {
items.push({
widget: 'dxButton',
name: 'workflowReset',
location: 'after',
locateInMenu: 'auto',
showText: 'always',
options: {
icon: 'revert',
text: toToolbarText(translate('::ListForms.ListForm.WorkflowReset')),
hint: translate('::ListForms.ListForm.WorkflowReset'),
visible: true,
disabled: true,
onClick: async () => {
const selectedRowKeys = (await Promise.resolve(
getSelectedRowKeys() as any,
)) as unknown[]
if (!selectedRowKeys?.length) {
toast.push(
<Notification type="warning" duration={2000}>
{translate('::ListForms.ListForm.SelectRecord')}
</Notification>,
{ placement: 'bottom-end' },
)
return
}
const selectedRows = ((await Promise.resolve(getSelectedRowsData() as any)) ||
[]) as Record<string, unknown>[]
if (
selectedRows.length === 0 ||
selectedRows.some((row) => isWorkflowNotStarted(row, workflowOptions))
) {
toast.push(
<Notification type="warning" duration={2500}>
{translate('::ListForms.ListForm.WorkflowNotStarted')}
</Notification>,
{ placement: 'bottom-end' },
)
return
}
if (
!grdOpt.keyFieldName ||
selectedRows.some(
(row) =>
row[grdOpt.keyFieldName!] === undefined || row[grdOpt.keyFieldName!] === null,
)
) {
toast.push(
<Notification type="danger" duration={3000}>
Liste formu anahtar alanı seçili kayıtlarda bulunamadı.
</Notification>,
{ placement: 'bottom-end' },
)
return
}
const keys = selectedRows.map((row) => row[grdOpt.keyFieldName!])
setToolbarModalData({
open: true,
content: (
<WorkflowResetConfirmDialog
keys={keys}
listFormCode={listFormCode}
onCancel={() => setToolbarModalData(undefined)}
onCompleted={() => {
refreshData()
setToolbarModalData(undefined)
}}
/>
),
})
},
},
})
}
items.push({
widget: 'dxButton',
name: 'workflowStart',
@ -716,6 +795,14 @@ export function isWorkflowToolbarItemDisabled(
)
}
if (itemName === 'workflowReset') {
return (
!workflowOptions.approvalIsResetWorkflow ||
selectedRowsData.length === 0 ||
selectedRowsData.some((row) => isWorkflowNotStarted(row, workflowOptions))
)
}
if (!itemName.startsWith('workflowApproval_')) {
return undefined
}
@ -778,6 +865,22 @@ export function updateWorkflowApprovalToolbarItems(
}
}
const workflowResetItemIndex = toolbarOptions.items
.map((item: any) => item.name)
.indexOf('workflowReset')
if (workflowResetItemIndex >= 0) {
const resetEnabled =
workflowOptions.approvalIsResetWorkflow === true &&
selectedRowsData.length > 0 &&
selectedRowsData.every((row) => !isWorkflowNotStarted(row, workflowOptions))
const resetOptionPath = `toolbar.items[${workflowResetItemIndex}].options.disabled`
const nextResetDisabled = !resetEnabled
if (component.option(resetOptionPath) !== nextResetDisabled) {
component.option(resetOptionPath, nextResetDisabled)
}
}
const currentUserIdentities = getCurrentUserWorkflowIdentities(currentUser)
approvalCriteria.forEach((criteria) => {
@ -808,6 +911,66 @@ export function updateWorkflowApprovalToolbarItems(
})
}
function WorkflowResetConfirmDialog({
keys,
listFormCode,
onCancel,
onCompleted,
}: {
keys: unknown[]
listFormCode: string
onCancel: () => void
onCompleted: () => void
}) {
const { translate } = useLocalization()
const [submitting, setSubmitting] = useState(false)
const reset = async () => {
setSubmitting(true)
try {
const results = await Promise.all(
keys.map((key) => workflowService.resetWorkflow(listFormCode, [key])),
)
showWorkflowToastMessages(results)
onCompleted()
} catch (error: any) {
toast.push(
<Notification type="danger" duration={3000}>
{error?.response?.data?.error?.message ||
error?.response?.data?.message ||
error?.message ||
'Workflow sifirlanamadi.'}
</Notification>,
{ placement: 'bottom-end' },
)
} finally {
setSubmitting(false)
}
}
return (
<>
<h5 className="mb-4">{translate('::ListForms.ListForm.WorkflowReset')}</h5>
<p className="mb-4">
{translate('::ListForms.ListForm.WorkflowResetMessage', { 0: keys.length })}
</p>
<div className="text-right mt-6">
<Button
className="ltr:mr-2 rtl:ml-2"
variant="plain"
disabled={submitting}
onClick={onCancel}
>
{translate('::Cancel')}
</Button>
<Button variant="solid" disabled={submitting} onClick={reset}>
{translate('::ListForms.ListForm.WorkflowReset')}
</Button>
</div>
</>
)
}
function WorkflowApprovalDecisionDialog({
criteriaTitle,
keys,