Grid Approval birden fazla kişi seçilebilir.
This commit is contained in:
parent
04e88fc969
commit
17e4716ee8
9 changed files with 194 additions and 38 deletions
|
|
@ -100,13 +100,11 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe
|
||||||
|
|
||||||
private string EncodePathAsId(string path)
|
private string EncodePathAsId(string path)
|
||||||
{
|
{
|
||||||
// Path'deki '/' karakterlerini '|' ile değiştir URL-safe hale getirmek için
|
|
||||||
return path.Replace("/", "|");
|
return path.Replace("/", "|");
|
||||||
}
|
}
|
||||||
|
|
||||||
private string DecodeIdAsPath(string id)
|
private string DecodeIdAsPath(string id)
|
||||||
{
|
{
|
||||||
// ID'deki '|' karakterlerini '/' ile geri değiştir
|
|
||||||
return id.Replace("|", "/");
|
return id.Replace("|", "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -357,6 +357,10 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
{
|
{
|
||||||
throw new UserFriendlyException("Seçili kayıt bu onay adımında beklemiyor.");
|
throw new UserFriendlyException("Seçili kayıt bu onay adımında beklemiyor.");
|
||||||
}
|
}
|
||||||
|
if (!IsCurrentUserWorkflowApprover(context, current))
|
||||||
|
{
|
||||||
|
throw new UserFriendlyException("Bu kayıt için onay yetkiniz yok.");
|
||||||
|
}
|
||||||
|
|
||||||
var update = new Dictionary<string, object>();
|
var update = new Dictionary<string, object>();
|
||||||
if (!context.Workflow.ApprovalUserFieldName.IsNullOrWhiteSpace())
|
if (!context.Workflow.ApprovalUserFieldName.IsNullOrWhiteSpace())
|
||||||
|
|
@ -548,7 +552,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
|
|
||||||
private async Task SendInformEmailAsync(WorkflowRunContext context, ListFormWorkflow node)
|
private async Task SendInformEmailAsync(WorkflowRunContext context, ListFormWorkflow node)
|
||||||
{
|
{
|
||||||
var recipientEmail = await ResolveApproverEmailAsync(node.Approver);
|
var recipientEmails = await ResolveApproverEmailsAsync(node.Approver);
|
||||||
var senderName = await settingProvider.GetOrNullAsync(SeedConsts.AbpSettings.Mailing.Default.DefaultFromDisplayName);
|
var senderName = await settingProvider.GetOrNullAsync(SeedConsts.AbpSettings.Mailing.Default.DefaultFromDisplayName);
|
||||||
var senderEmail = await settingProvider.GetOrNullAsync(SeedConsts.AbpSettings.Mailing.Default.DefaultFromAddress);
|
var senderEmail = await settingProvider.GetOrNullAsync(SeedConsts.AbpSettings.Mailing.Default.DefaultFromAddress);
|
||||||
KeyValuePair<string, string>? sender = null;
|
KeyValuePair<string, string>? sender = null;
|
||||||
|
|
@ -557,11 +561,14 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
sender = new KeyValuePair<string, string>(senderName, senderEmail);
|
sender = new KeyValuePair<string, string>(senderName, senderEmail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var body = BuildInformEmailBody(context, node, await BuildPreviousWorkflowNotesHtmlAsync(context));
|
||||||
|
foreach (var recipientEmail in recipientEmails)
|
||||||
|
{
|
||||||
var result = await erpEmailSender.SendEmailAsync(
|
var result = await erpEmailSender.SendEmailAsync(
|
||||||
recipientEmail,
|
recipientEmail,
|
||||||
sender,
|
sender,
|
||||||
new { },
|
new { },
|
||||||
BuildInformEmailBody(context, node, await BuildPreviousWorkflowNotesHtmlAsync(context)),
|
body,
|
||||||
$"Workflow Bilgilendirme: {node.Title}",
|
$"Workflow Bilgilendirme: {node.Title}",
|
||||||
null,
|
null,
|
||||||
true);
|
true);
|
||||||
|
|
@ -570,9 +577,26 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
{
|
{
|
||||||
throw new UserFriendlyException($"Bilgilendirme maili gonderilemedi: {result.ErrorMessage}");
|
throw new UserFriendlyException($"Bilgilendirme maili gonderilemedi: {result.ErrorMessage}");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<List<string>> ResolveApproverEmailsAsync(string approver)
|
||||||
|
{
|
||||||
|
var emails = new List<string>();
|
||||||
|
foreach (var item in SplitWorkflowApprovers(approver))
|
||||||
|
{
|
||||||
|
emails.Add(await ResolveApproverEmailAsync(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emails.Count == 0)
|
||||||
|
{
|
||||||
|
throw new UserFriendlyException("Bilgilendirme dugumu icin approver tanimli degil.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return emails;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<string> ResolveApproverEmailAsync(string approver)
|
private async Task<string> ResolveApproverEmailAsync(string approver)
|
||||||
{
|
{
|
||||||
if (approver.IsNullOrWhiteSpace())
|
if (approver.IsNullOrWhiteSpace())
|
||||||
|
|
@ -594,6 +618,45 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
return user.Email;
|
return user.Email;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsCurrentUserWorkflowApprover(WorkflowRunContext context, ListFormWorkflow current)
|
||||||
|
{
|
||||||
|
if (context.Workflow.ApprovalUserFieldName.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var approverValue = GetRowValue(context.Row, context.Workflow.ApprovalUserFieldName)?.ToString();
|
||||||
|
if (approverValue.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
approverValue = current.Approver;
|
||||||
|
}
|
||||||
|
|
||||||
|
var approvers = SplitWorkflowApprovers(approverValue)
|
||||||
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (approvers.Count == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetCurrentUserWorkflowIdentities().Any(approvers.Contains);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<string> GetCurrentUserWorkflowIdentities()
|
||||||
|
{
|
||||||
|
return new[] { CurrentUser.UserName, CurrentUser.Email, CurrentUser.Name }
|
||||||
|
.Where(identity => !identity.IsNullOrWhiteSpace())
|
||||||
|
.Select(identity => identity.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<string> SplitWorkflowApprovers(string value)
|
||||||
|
{
|
||||||
|
return (value ?? string.Empty)
|
||||||
|
.Split(PlatformConsts.MultiValueDelimiter, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(item => item.Trim())
|
||||||
|
.Where(item => !item.IsNullOrWhiteSpace());
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<string> BuildPreviousWorkflowNotesHtmlAsync(WorkflowRunContext context)
|
private async Task<string> BuildPreviousWorkflowNotesHtmlAsync(WorkflowRunContext context)
|
||||||
{
|
{
|
||||||
var key = context.Keys?.FirstOrDefault()?.ToString();
|
var key = context.Keys?.FirstOrDefault()?.ToString();
|
||||||
|
|
@ -633,7 +696,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
var keyText = string.Join(", ", context.Keys.Select(key => WebUtility.HtmlEncode(key?.ToString() ?? string.Empty)));
|
var keyText = string.Join(", ", context.Keys.Select(key => WebUtility.HtmlEncode(key?.ToString() ?? string.Empty)));
|
||||||
var listFormCode = WebUtility.HtmlEncode(context.ListFormCode ?? string.Empty);
|
var listFormCode = WebUtility.HtmlEncode(context.ListFormCode ?? string.Empty);
|
||||||
var nodeTitle = WebUtility.HtmlEncode(node.Title ?? string.Empty);
|
var nodeTitle = WebUtility.HtmlEncode(node.Title ?? string.Empty);
|
||||||
var recipient = WebUtility.HtmlEncode(node.Approver ?? string.Empty);
|
var recipient = WebUtility.HtmlEncode(FormatWorkflowApprovers(node.Approver));
|
||||||
var processRows = BuildWorkflowNoteContent(context.WorkflowNoteRows);
|
var processRows = BuildWorkflowNoteContent(context.WorkflowNoteRows);
|
||||||
var previousNotesSection = previousWorkflowNotesHtml.IsNullOrWhiteSpace()
|
var previousNotesSection = previousWorkflowNotesHtml.IsNullOrWhiteSpace()
|
||||||
? string.Empty
|
? string.Empty
|
||||||
|
|
@ -697,7 +760,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
|
|
||||||
if (!node.Approver.IsNullOrWhiteSpace())
|
if (!node.Approver.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
context.WorkflowNoteRows.Add((node.Kind == "Inform" ? "Inform: " : "Approver: ", node.Approver));
|
context.WorkflowNoteRows.Add((node.Kind == "Inform" ? "Inform: " : "Approver: ", FormatWorkflowApprovers(node.Approver)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -790,7 +853,12 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
|
||||||
{
|
{
|
||||||
return userName.IsNullOrWhiteSpace()
|
return userName.IsNullOrWhiteSpace()
|
||||||
? localizer["ListForms.ListForm.Workflow.UndefinedUser"].Value
|
? localizer["ListForms.ListForm.Workflow.UndefinedUser"].Value
|
||||||
: userName;
|
: FormatWorkflowApprovers(userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatWorkflowApprovers(string value)
|
||||||
|
{
|
||||||
|
return string.Join(", ", SplitWorkflowApprovers(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task InsertWorkflowNoteAsync(
|
private async Task InsertWorkflowNoteAsync(
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,9 @@ public static class AbpPermissionsExtensions
|
||||||
|
|
||||||
// 🔹 Gereksiz boşlukları temizle
|
// 🔹 Gereksiz boşlukları temizle
|
||||||
var cleaned = string.Join(
|
var cleaned = string.Join(
|
||||||
'|',
|
"|",
|
||||||
menuGroup
|
menuGroup
|
||||||
.Split('|', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
.Split(PlatformConsts.MultiValueDelimiter, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -44,7 +44,7 @@ public static class AbpPermissionsExtensions
|
||||||
|
|
||||||
// 🔹 "Erp|Kurs" -> ["Erp", "Kurs"]
|
// 🔹 "Erp|Kurs" -> ["Erp", "Kurs"]
|
||||||
return value
|
return value
|
||||||
.Split('|', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
.Split(PlatformConsts.MultiValueDelimiter, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -479,11 +479,7 @@ public class SelectQueryManager : PlatformDomainService, ISelectQueryManager
|
||||||
// hem de ApprovalUserFieldName alanı CurrentUserName'e eşit olan kayıtları getirmek istiyoruz,
|
// hem de ApprovalUserFieldName alanı CurrentUserName'e eşit olan kayıtları getirmek istiyoruz,
|
||||||
// Boş olanları getirmemizin sebebi workflow start edebilmektir.
|
// Boş olanları getirmemizin sebebi workflow start edebilmektir.
|
||||||
// İlk kayıt eklenince onaylayacak kişi atanmaz, böylece o kayıt onaysız olarak kalmaz ve workflow başlatılabilir olur.
|
// İlk kayıt eklenince onaylayacak kişi atanmaz, böylece o kayıt onaysız olarak kalmaz ve workflow başlatılabilir olur.
|
||||||
whereParts.Add(
|
whereParts.Add(GetWorkflowApproverFilter(workflow.ApprovalUserFieldName));
|
||||||
$"(\"{workflow.ApprovalUserFieldName}\" = '{CurrentUser.UserName}' " +
|
|
||||||
$"OR \"{workflow.ApprovalUserFieldName}\" IS NULL " +
|
|
||||||
$"OR \"{workflow.ApprovalUserFieldName}\" = '')"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -495,6 +491,38 @@ public class SelectQueryManager : PlatformDomainService, ISelectQueryManager
|
||||||
return whereParts;
|
return whereParts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetWorkflowApproverFilter(string approvalUserFieldName)
|
||||||
|
{
|
||||||
|
if (approvalUserFieldName.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return "1 = 1";
|
||||||
|
}
|
||||||
|
|
||||||
|
var field = $"\"{approvalUserFieldName}\"";
|
||||||
|
var identities = new[] { CurrentUser.UserName, CurrentUser.Email, CurrentUser.Name }
|
||||||
|
.Where(identity => !identity.IsNullOrWhiteSpace())
|
||||||
|
.Select(EscapeSqlLiteral)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (identities.Count == 0)
|
||||||
|
{
|
||||||
|
return $"({field} IS NULL OR {field} = '')";
|
||||||
|
}
|
||||||
|
|
||||||
|
var identityList = string.Join(",", identities.Select(identity => $"'{identity}'"));
|
||||||
|
var approverMatch = DataSourceType == DataSourceTypeEnum.Postgresql
|
||||||
|
? $"EXISTS (SELECT 1 FROM unnest(string_to_array(CAST({field} AS TEXT), '{PlatformConsts.MultiValueDelimiter}')) AS split(value) WHERE btrim(split.value) IN ({identityList}))"
|
||||||
|
: $"EXISTS (SELECT 1 FROM STRING_SPLIT(CAST({field} AS NVARCHAR(MAX)), N'{PlatformConsts.MultiValueDelimiter}') AS split WHERE LTRIM(RTRIM(split.value)) IN ({identityList}))";
|
||||||
|
|
||||||
|
return $"({approverMatch} OR {field} IS NULL OR {field} = '')";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string EscapeSqlLiteral(string value)
|
||||||
|
{
|
||||||
|
return (value ?? string.Empty).Replace("'", "''");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> FieldName operator Id seklinde olan filtreleri ayirir
|
/// <summary> FieldName operator Id seklinde olan filtreleri ayirir
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filter"></param>
|
/// <param name="filter"></param>
|
||||||
|
|
@ -829,8 +857,7 @@ public class SelectQueryManager : PlatformDomainService, ISelectQueryManager
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Birden fazla → '|' ile birleştir
|
argumentExpression = string.Join(PlatformConsts.MultiValueDelimiter, argumentFields.Select(f => $"[{f}]"));
|
||||||
argumentExpression = string.Join(" + '|' + ", argumentFields.Select(f => $"[{f}]"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select sütunlarını oluştur
|
// Select sütunlarını oluştur
|
||||||
|
|
|
||||||
|
|
@ -1854,7 +1854,7 @@ internal static class DynamicReportImageHelper
|
||||||
|
|
||||||
private static bool IsImageDelimiter(string text, int index)
|
private static bool IsImageDelimiter(string text, int index)
|
||||||
{
|
{
|
||||||
return text[index] == '|' ||
|
return text[index] == PlatformConsts.MultiValueDelimiter ||
|
||||||
text[index] == '\n' ||
|
text[index] == '\n' ||
|
||||||
text[index] == '\r' ||
|
text[index] == '\r' ||
|
||||||
(text[index] == ',' && !IsDataImageComma(text, index));
|
(text[index] == ',' && !IsDataImageComma(text, index));
|
||||||
|
|
@ -1868,7 +1868,7 @@ internal static class DynamicReportImageHelper
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var previousDelimiterIndex = text.LastIndexOfAny(['|', '\r', '\n'], index);
|
var previousDelimiterIndex = text.LastIndexOfAny([PlatformConsts.MultiValueDelimiter, '\r', '\n'], index);
|
||||||
return previousDelimiterIndex < dataImageIndex &&
|
return previousDelimiterIndex < dataImageIndex &&
|
||||||
text.IndexOf(";base64,", dataImageIndex, index - dataImageIndex + 1, StringComparison.OrdinalIgnoreCase) >= 0;
|
text.IndexOf(";base64,", dataImageIndex, index - dataImageIndex + 1, StringComparison.OrdinalIgnoreCase) >= 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useLocalization } from '../hooks/useLocalization'
|
import { MULTIVALUE_DELIMITER } from '@/constants/app.constant'
|
||||||
import { getNodeHeight, nodeSize } from './workflowConstants'
|
import { getNodeHeight, nodeSize } from './workflowConstants'
|
||||||
import type {
|
import type {
|
||||||
CompareOutcomeDto,
|
CompareOutcomeDto,
|
||||||
|
|
@ -179,10 +179,10 @@ export function collectLinks(criteria: WorkflowCriteriaDto[]) {
|
||||||
field: 'nextOnReject',
|
field: 'nextOnReject',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return assignLinkSlots(links, criteria)
|
return assignLinkSlots(links)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assignLinkSlots(links: WorkflowLink[], criteria: WorkflowCriteriaDto[]) {
|
export function assignLinkSlots(links: WorkflowLink[]) {
|
||||||
const endpointGroups = new Map<string, Endpoint[]>()
|
const endpointGroups = new Map<string, Endpoint[]>()
|
||||||
const addEndpoint = (nodeId: string, side: string, endpoint: Endpoint) => {
|
const addEndpoint = (nodeId: string, side: string, endpoint: Endpoint) => {
|
||||||
const key = `${nodeId}:${side}`
|
const key = `${nodeId}:${side}`
|
||||||
|
|
@ -588,11 +588,22 @@ export function criteriaSummary(item: WorkflowCriteriaDto) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (item.kind === 'Approval' || item.kind === 'Inform') {
|
if (item.kind === 'Approval' || item.kind === 'Inform') {
|
||||||
return `${item.title} ${item.approver ? `- ${item.approver}` : ''}`
|
return `${item.title} ${item.approver ? `- ${formatWorkflowApprovers(item.approver)}` : ''}`
|
||||||
}
|
}
|
||||||
return item.title
|
return item.title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function splitWorkflowApprovers(value?: string | null) {
|
||||||
|
return String(value ?? '')
|
||||||
|
.split(MULTIVALUE_DELIMITER)
|
||||||
|
.map((item) => item.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatWorkflowApprovers(value?: string | null) {
|
||||||
|
return splitWorkflowApprovers(value).join(', ')
|
||||||
|
}
|
||||||
|
|
||||||
export function targetTitle(criteria: WorkflowCriteriaDto[], id?: string | null) {
|
export function targetTitle(criteria: WorkflowCriteriaDto[], id?: string | null) {
|
||||||
if (!id) return '-'
|
if (!id) return '-'
|
||||||
const item = criteria.find((candidate) => candidate.id === id)
|
const item = criteria.find((candidate) => candidate.id === id)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import {
|
||||||
WidgetEditDto,
|
WidgetEditDto,
|
||||||
WorkflowDto,
|
WorkflowDto,
|
||||||
} from '@/proxy/form/models'
|
} from '@/proxy/form/models'
|
||||||
|
import { formatWorkflowApprovers } from '@/utils/workflow/workflowHelpers'
|
||||||
|
|
||||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -559,7 +560,7 @@ const WizardStep7 = ({
|
||||||
{(criteria.kind === 'Approval' || criteria.kind === 'Inform') &&
|
{(criteria.kind === 'Approval' || criteria.kind === 'Inform') &&
|
||||||
criteria.approver && (
|
criteria.approver && (
|
||||||
<div className="text-[11px] text-gray-500 dark:text-gray-400">
|
<div className="text-[11px] text-gray-500 dark:text-gray-400">
|
||||||
Approver: {criteria.approver}
|
Approver: {formatWorkflowApprovers(criteria.approver)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,14 @@ import {
|
||||||
compareOutcomeRuleText,
|
compareOutcomeRuleText,
|
||||||
criteriaSummary,
|
criteriaSummary,
|
||||||
emptyCompareOutcome1,
|
emptyCompareOutcome1,
|
||||||
|
splitWorkflowApprovers,
|
||||||
targetTitle,
|
targetTitle,
|
||||||
} from '@/utils/workflow/workflowHelpers'
|
} from '@/utils/workflow/workflowHelpers'
|
||||||
import type { CompareOutcomeDto, WorkflowCriteriaDto } from '@/services/workflow.service'
|
import type { CompareOutcomeDto, WorkflowCriteriaDto } from '@/services/workflow.service'
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import type { DatabaseColumnDto } from '@/proxy/sql-query-manager/models'
|
import type { DatabaseColumnDto } from '@/proxy/sql-query-manager/models'
|
||||||
|
import { MULTIVALUE_DELIMITER } from '@/constants/app.constant'
|
||||||
|
|
||||||
type WorkflowCriteriaProps = {
|
type WorkflowCriteriaProps = {
|
||||||
criteria: WorkflowCriteriaDto[]
|
criteria: WorkflowCriteriaDto[]
|
||||||
|
|
@ -265,7 +267,7 @@ export function WorkflowCriteria({
|
||||||
asterisk
|
asterisk
|
||||||
label={translate('::App.Listform.ListformField.Approver')}
|
label={translate('::App.Listform.ListformField.Approver')}
|
||||||
>
|
>
|
||||||
<SelectField
|
<MultiSelectField
|
||||||
required
|
required
|
||||||
options={userList}
|
options={userList}
|
||||||
value={formValues.approver}
|
value={formValues.approver}
|
||||||
|
|
@ -527,6 +529,48 @@ function SelectField({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function MultiSelectField({
|
||||||
|
options,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
required = false,
|
||||||
|
}: {
|
||||||
|
options: SelectBoxOption[]
|
||||||
|
value: string
|
||||||
|
onChange: (value: string) => void
|
||||||
|
required?: boolean
|
||||||
|
}) {
|
||||||
|
const values = splitWorkflowApprovers(value)
|
||||||
|
const selectedOptions = options.filter((option) => values.includes(String(option.value)))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Select
|
||||||
|
isMulti
|
||||||
|
options={options}
|
||||||
|
value={selectedOptions}
|
||||||
|
onChange={(selected: any) =>
|
||||||
|
onChange(
|
||||||
|
(Array.isArray(selected) ? selected : [])
|
||||||
|
.map((option) => option?.value)
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(MULTIVALUE_DELIMITER),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{required && (
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
tabIndex={-1}
|
||||||
|
className="pointer-events-none h-0 w-0 opacity-0"
|
||||||
|
value={value}
|
||||||
|
onChange={() => undefined}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function criteriaSummaryContent(item: WorkflowCriteriaDto) {
|
function criteriaSummaryContent(item: WorkflowCriteriaDto) {
|
||||||
if (item.kind === 'Compare') {
|
if (item.kind === 'Compare') {
|
||||||
const outcomes = item.compareOutcomes || []
|
const outcomes = item.compareOutcomes || []
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,18 @@ import { usePermission } from '@/utils/hooks/usePermission'
|
||||||
import { DataGridTypes } from 'devextreme-react/data-grid'
|
import { DataGridTypes } from 'devextreme-react/data-grid'
|
||||||
import { ToolbarItem } from 'devextreme/ui/data_grid_types'
|
import { ToolbarItem } from 'devextreme/ui/data_grid_types'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
import type { ReactNode } from 'react'
|
||||||
import { useDialogContext } from '../shared/DialogContext'
|
import { useDialogContext } from '../shared/DialogContext'
|
||||||
import { usePWA } from '@/utils/hooks/usePWA'
|
import { usePWA } from '@/utils/hooks/usePWA'
|
||||||
import { layoutTypes, ListViewLayoutType } from '../admin/listForm/edit/types'
|
import { layoutTypes, ListViewLayoutType } from '../admin/listForm/edit/types'
|
||||||
import { useStoreState } from '@/store'
|
import { useStoreState } from '@/store'
|
||||||
import { workflowService } from '@/services/workflow.service'
|
import { workflowService } from '@/services/workflow.service'
|
||||||
import type { WorkflowRunResultDto } from '@/services/workflow.service'
|
import type { WorkflowRunResultDto } from '@/services/workflow.service'
|
||||||
|
import { splitWorkflowApprovers } from '@/utils/workflow/workflowHelpers'
|
||||||
|
|
||||||
type ToolbarModalData = {
|
type ToolbarModalData = {
|
||||||
open: boolean
|
open: boolean
|
||||||
content?: JSX.Element
|
content?: ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
const showWorkflowToastMessages = (results: WorkflowRunResultDto | WorkflowRunResultDto[]) => {
|
const showWorkflowToastMessages = (results: WorkflowRunResultDto | WorkflowRunResultDto[]) => {
|
||||||
|
|
@ -626,8 +628,13 @@ function isWorkflowApprovalCriteriaActive(
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const approver = normalizeWorkflowValue(row?.[workflowOptions.approvalUserFieldName])
|
const approvers = splitWorkflowApprovers(
|
||||||
return currentUserIdentities.some((identity) => normalizeWorkflowValue(identity) === approver)
|
String(row?.[workflowOptions.approvalUserFieldName] ?? ''),
|
||||||
|
).map(normalizeWorkflowValue)
|
||||||
|
|
||||||
|
return currentUserIdentities.some((identity) =>
|
||||||
|
approvers.includes(normalizeWorkflowValue(identity)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeWorkflowValue(value: unknown) {
|
function normalizeWorkflowValue(value: unknown) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue