ExtraFilterEditDto ve Admin Tarafı

This commit is contained in:
Sedat ÖZTÜRK 2025-09-17 23:50:56 +03:00
parent defbf93165
commit 9f5c8276da
13 changed files with 538 additions and 3 deletions

View file

@ -106,7 +106,6 @@ public class GridOptionsEditDto : GridOptionsDto
set { WidgetsJson = JsonSerializer.Serialize(value); }
}
[JsonIgnore]
public List<ExtraFilterEditDto> ExtraFilterEditDto
{
get

View file

@ -49,4 +49,5 @@ public class ListFormEditTabs
public const string WidgetForm = "widget";
public const string Fields = "fields";
public const string Customization = "customization";
public const string ExtraFilterForm = "extraFilter";
}

View file

@ -11,4 +11,5 @@ public class CrudFieldsDefaultValueJsonItemDto
public EditingFormDto ItemEditingForm { get; set; }
public SubFormDto ItemSubForm { get; set; }
public WidgetEditDto ItemWidget { get; set; }
public ExtraFilterEditDto ItemExtraFilter { get; set; }
}

View file

@ -41,6 +41,7 @@ public class ListFormJsonRowAppService : PlatformAppService
ListFormEditTabs.CommandColumnsJsonRow => listForm.CommandColumnJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<CommandColumnDto>>(listForm.CommandColumnJson),
ListFormEditTabs.SubFormJsonRow => listForm.SubFormsJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<SubFormDto>>(listForm.SubFormsJson),
ListFormEditTabs.WidgetForm => listForm.WidgetsJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<WidgetEditDto>>(listForm.WidgetsJson),
ListFormEditTabs.ExtraFilterForm => listForm.ExtraFilterJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<ExtraFilterEditDto>>(listForm.ExtraFilterJson),
_ => throw new UserFriendlyException(L[AppErrorCodes.ParameterNotValid]),
};
}
@ -77,6 +78,9 @@ public class ListFormJsonRowAppService : PlatformAppService
case ListFormEditTabs.WidgetForm:
listForm.WidgetsJson = CreateRow(listForm.WidgetsJson, ObjectMapper.Map<WidgetEditDto, Widget>(model.ItemWidget));
break;
case ListFormEditTabs.ExtraFilterForm:
listForm.ExtraFilterJson = CreateRow(listForm.ExtraFilterJson, ObjectMapper.Map<ExtraFilterEditDto, ExtraFilter>(model.ItemExtraFilter));
break;
default:
throw new UserFriendlyException(L[AppErrorCodes.ParameterNotValid]);
}
@ -116,6 +120,9 @@ public class ListFormJsonRowAppService : PlatformAppService
case ListFormEditTabs.WidgetForm:
listForm.WidgetsJson = UpdateRow(listForm.WidgetsJson, ObjectMapper.Map<WidgetEditDto, Widget>(model.ItemWidget), model.Index);
break;
case ListFormEditTabs.ExtraFilterForm:
listForm.ExtraFilterJson = UpdateRow(listForm.ExtraFilterJson, ObjectMapper.Map<ExtraFilterEditDto, ExtraFilter>(model.ItemExtraFilter), model.Index);
break;
default:
throw new UserFriendlyException(L[AppErrorCodes.ParameterNotValid]);
}
@ -155,6 +162,9 @@ public class ListFormJsonRowAppService : PlatformAppService
case ListFormEditTabs.WidgetForm:
listForm.WidgetsJson = DeleteRow<WidgetEditDto>(listForm.WidgetsJson, index);
break;
case ListFormEditTabs.ExtraFilterForm:
listForm.ExtraFilterJson = DeleteRow<ExtraFilterEditDto>(listForm.ExtraFilterJson, index);
break;
default:
throw new UserFriendlyException(L[AppErrorCodes.ParameterNotValid]);
}

View file

@ -28,6 +28,7 @@ public class ListFormAutoMapperProfile : Profile
CreateMap<ExtraFilterDto, ExtraFilter>().ReverseMap();
CreateMap<ExtraFilterItemsDto, ExtraFilterItems>().ReverseMap();
CreateMap<ExtraFilterEditDto, ExtraFilterDto>().ReverseMap();
CreateMap<ExtraFilterEditDto, ExtraFilter>().ReverseMap();
CreateMap<ListFormImport, ListFormsImportDto>();
CreateMap<ListFormImportExecute, ListFormImportExecuteDto>();

View file

@ -6272,7 +6272,7 @@
{
"ParentCode": null,
"Code": "App.Coordinator",
"DisplayName": "Koordinatör",
"DisplayName": "App.Coordinator",
"Order": 500,
"Url": null,
"Icon": "FcCollaboration",
@ -12256,6 +12256,42 @@
"en": "Data Source Code",
"tr": "Veri Kaynağı Kodu"
},
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.ExtraFilters",
"en": "Extra Filters",
"tr": "Ekstra Filtreler"
},
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.ExtraFieldName",
"en": "Field Name",
"tr": "Alan Adı"
},
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.ExtraCaption",
"en": "Caption",
"tr": "Başlık"
},
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.ExtraOperator",
"en": "Operator",
"tr": "Operatör"
},
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.ExtraDefaultValue",
"en": "Default Value",
"tr": "Varsayılan Değer"
},
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.ExtraSqlQuery",
"en": "Sql Query",
"tr": "Sql Sorgusu"
},
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.IsTenant",

View file

@ -8,6 +8,8 @@ public class ExtraFilter : ValueObject
public string FieldName { get; set; }
public string Caption { get; set; }
public string Operator { get; set; }
public string DefaultValue { get; set; }
public string SqlQuery { get; set; }
public ExtraFilterItems[] Items { get; set; }
protected override IEnumerable<object> GetAtomicValues()
@ -15,6 +17,8 @@ public class ExtraFilter : ValueObject
yield return FieldName;
yield return Caption;
yield return Operator;
yield return DefaultValue;
yield return SqlQuery;
yield return Items;
}
}

View file

@ -1,6 +1,7 @@
import {
CommandColumnDto,
EditingFormDto,
ExtraFilterEditDto,
FieldsDefaultValueDto,
SelectCommandTypeEnum,
SubFormDto,
@ -37,6 +38,7 @@ export interface ListFormJsonRowDto {
itemEditingForm?: EditingFormDto
itemSubForm?: SubFormDto
itemWidget?: WidgetEditDto
itemExtraFilter?: ExtraFilterEditDto
}
export const ListFormEditTabs = {
@ -78,4 +80,5 @@ export const ListFormEditTabs = {
Widget: 'widget',
Fields: 'fields',
Customization: 'customization',
ExtraFilter: 'extraFilter',
} as const

View file

@ -505,6 +505,7 @@ export interface GridOptionsEditDto extends GridOptionsDto, Record<string, any>
formFieldsDefaultValueDto: FieldsDefaultValueDto[]
widgetsJson?: string
widgetsDto: WidgetEditDto[]
extraFilterEditDto: ExtraFilterEditDto[]
}
export interface GridPagerOptionDto {
@ -730,10 +731,14 @@ export interface ExtraFilterDto {
caption: string
operator: string
defaultValue?: string
items: ExtraFilterItemsDto[]
items?: ExtraFilterItemsDto[]
}
export interface ExtraFilterItemsDto {
value: string
text: string
}
export interface ExtraFilterEditDto extends ExtraFilterDto {
sqlQuery: string
}

View file

@ -33,6 +33,7 @@ import { getListFormCustomizations } from '@/services/admin/list-form-customizat
import { Container } from '@/components/shared'
import { ROUTES_ENUM } from '@/routes/route.constant'
import FormTabWidgets from './FormTabWidgets'
import FormTabExtraFilters from './FormTabExtraFilters'
export interface FormEditProps {
onSubmit: (
@ -193,6 +194,7 @@ const FormEdit = () => {
<TabNav value="customization">
{translate('::ListForms.ListFormEdit.TabCustomization')}
</TabNav>
<TabNav value="extrafilter">{translate('::ListForms.ListFormEdit.ExtraFilters')}</TabNav>
</TabList>
<TabContent value="details">
<FormTabDetails onSubmit={onSubmit} />
@ -253,6 +255,9 @@ const FormEdit = () => {
getListFormCustomizations={getCustomizations}
></FormCustomization>
</TabContent>
<TabContent value="extrafilter">
<FormTabExtraFilters listFormCode={listFormCode} />
</TabContent>
</Tabs>
</Container>
) : (

View file

@ -0,0 +1,127 @@
import { Container } from '@/components/shared'
import { Button, Card, Table } from '@/components/ui'
import TBody from '@/components/ui/Table/TBody'
import THead from '@/components/ui/Table/THead'
import Td from '@/components/ui/Table/Td'
import Th from '@/components/ui/Table/Th'
import Tr from '@/components/ui/Table/Tr'
import { ListFormEditTabs } from '@/proxy/admin/list-form/models'
import { useStoreState } from '@/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { useState } from 'react'
import { FaEdit, FaFileMedical, FaTrash } from 'react-icons/fa'
import { JsonRowDialogData } from './json-row-operations/types'
import JsonRowOpDialogExtraFilter from './json-row-operations/JsonRowOpDialogExtraFilter'
function FormTabExtraFilters(props: { listFormCode: string }) {
const [isJsonRowOpDialogOpen, setIsJsonRowOpDialogOpen] = useState(false)
const [jsonRowOpModalData, setJsonRowOpModalData] = useState<JsonRowDialogData>()
const { translate } = useLocalization()
const initialValues = useStoreState((s) => s.admin.listFormValues)
if (!initialValues) {
return null
}
return (
<Container>
<Card
className="my-2"
bodyClass="p-0"
header={translate('::ListForms.ListFormEdit.ExtraFilters')}
headerExtra={translate('::ListForms.ListFormEdit.ExtraFiltersDescription')}
>
<Table compact>
<THead>
<Tr>
<Th className="text-center min-w-[100px]">
<Button
shape="circle"
variant="plain"
type="button"
size="xs"
title="Add"
icon={<FaFileMedical />}
onClick={async (e) => {
e.preventDefault()
setJsonRowOpModalData({
tabName: ListFormEditTabs.ExtraFilter,
operation: 'create',
id: initialValues.id ?? '',
index: -1,
})
setIsJsonRowOpDialogOpen(true)
}}
/>
</Th>
<Th>{translate('::ListForms.ListFormEdit.ExtraFieldName')}</Th>
<Th>{translate('::ListForms.ListFormEdit.ExtraCaption')}</Th>
<Th>{translate('::ListForms.ListFormEdit.ExtraOperator')}</Th>
<Th>{translate('::ListForms.ListFormEdit.ExtraDefaultValue')}</Th>
<Th>{translate('::ListForms.ListFormEdit.ExtraSqlQuery')}</Th>
</Tr>
</THead>
<TBody>
{initialValues.extraFilterEditDto.map((row, index) => (
<Tr key={index}>
<Td>
<div className="flex-wrap inline-flex xl:flex items-center gap-2">
<Button
shape="circle"
variant="plain"
type="button"
size="xs"
title="Edit"
icon={<FaEdit />}
onClick={() => {
setJsonRowOpModalData({
tabName: ListFormEditTabs.ExtraFilter,
operation: 'update',
id: initialValues.id ?? '',
index,
extraFilterValues: initialValues.extraFilterEditDto[index],
})
setIsJsonRowOpDialogOpen(true)
}}
/>
<Button
shape="circle"
variant="plain"
type="button"
size="xs"
title="Delete"
icon={<FaTrash />}
onClick={() => {
setJsonRowOpModalData({
tabName: ListFormEditTabs.ExtraFilter,
operation: 'delete',
id: initialValues.id ?? '',
index,
})
setIsJsonRowOpDialogOpen(true)
}}
/>
</div>
</Td>
<Td>{row.fieldName}</Td>
<Td>{row.caption}</Td>
<Td>{row.operator}</Td>
<Td>{row.defaultValue}</Td>
<Td>{row.sqlQuery}</Td>
</Tr>
))}
</TBody>
</Table>
</Card>
<JsonRowOpDialogExtraFilter
listFormCode={props.listFormCode}
isOpen={isJsonRowOpDialogOpen}
setIsOpen={setIsJsonRowOpDialogOpen}
data={jsonRowOpModalData}
setData={setJsonRowOpModalData}
/>
</Container>
)
}
export default FormTabExtraFilters

View file

@ -0,0 +1,341 @@
import {
Button,
Dialog,
FormContainer,
FormItem,
Input,
Notification,
Select,
toast,
} from '@/components/ui'
import { ListFormJsonRowDto } from '@/proxy/admin/list-form/models'
import { SelectBoxOption } from '@/shared/types'
import { useStoreActions, useStoreState } from '@/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { Field, FieldProps, Form, Formik } from 'formik'
import { Dispatch, SetStateAction, useEffect, useState } from 'react'
import { object, string } from 'yup'
import { JsonRowDialogData } from './types'
import {
deleteListFormJsonRow,
getListFormJsonRow,
postListFormJsonRow,
putListFormJsonRow,
} from '@/services/admin/list-form.service'
import { cascadeFilterOperator, colSpanOptions } from '../options'
import CreatableSelect from 'react-select/creatable'
import { getListFormFields } from '@/services/admin/list-form-field.service'
import { groupBy } from 'lodash'
const schema = object().shape({
fieldName: string().required('Field Name Required'),
caption: string().required('Caption Required'),
operator: string().required('Operator Required'),
defaultValue: string().required('Value Required'),
sqlQuery: string().required('SQL Query Required'),
})
function JsonRowOpDialogExtraFilter({
listFormCode,
isOpen,
setIsOpen,
data,
setData,
}: {
listFormCode: string
isOpen: boolean
setIsOpen: Dispatch<SetStateAction<boolean>>
data: JsonRowDialogData | undefined
setData: Dispatch<SetStateAction<JsonRowDialogData | undefined>>
}) {
const [permissionOptions, setPermissionOptions] = useState<SelectBoxOption[]>([])
const { translate } = useLocalization()
const permissions: Record<string, boolean> | undefined = useStoreState(
(state) => state.abpConfig.config?.auth.grantedPolicies,
)
const [fieldList, setFieldList] = useState<SelectBoxOption[]>([])
useEffect(() => {
if (permissions) {
setPermissionOptions(
Object.keys(permissions).map((key) => {
return {
value: key,
label: key,
}
}),
)
}
}, [permissions])
const { setJsonValue } = useStoreActions((a) => a.admin)
const handleClose = async (e?: any) => {
if (e) {
e.preventDefault()
}
if (data) {
const resp = await getListFormJsonRow(data.id, data.tabName)
setJsonValue({ field: 'extraFilterEditDto', data: resp.data })
}
setData(undefined)
setIsOpen(false)
}
const getFields = async () => {
if (!listFormCode) {
return
}
try {
const resp = await getListFormFields({
listFormCode,
sorting: 'ListOrderNo',
maxResultCount: 1000,
})
if (resp.data?.items) {
const fieldNames = groupBy(resp?.data?.items, 'fieldName')
setFieldList(
Object.keys(fieldNames).map((a) => ({
value: a,
label: a,
})),
)
}
} catch (error: any) {
toast.push(
<Notification type="danger" duration={2000}>
Alanlar getirilemedi
{error.toString()}
</Notification>,
{
placement: 'top-end',
},
)
}
}
useEffect(() => {
if (isOpen && data) {
getFields()
}
}, [isOpen, data])
if (!data || !fieldList?.length) {
return null
}
return (
<Dialog
id="extraFilterOperation"
isOpen={isOpen}
preventScroll={true}
onClose={handleClose}
onRequestClose={handleClose}
width={data.operation === 'create' || data.operation === 'update' ? 'sm' : '450px'}
>
{(data.operation === 'create' || data.operation === 'update') && (
<>
<h5 className="mb-4">{data.index === -1 ? 'Add' : 'Update'}</h5>
<Formik
initialValues={
data.extraFilterValues ?? {
fieldName: '',
caption: '',
operator: '=',
defaultValue: '',
sqlQuery: '',
}
}
validationSchema={schema}
onSubmit={async (values, { setSubmitting }) => {
setSubmitting(true)
try {
const input: ListFormJsonRowDto = {
index: data.index,
fieldName: data.tabName,
itemExtraFilter: values,
}
if (data.index === -1) {
await postListFormJsonRow(data.id, input)
} else {
await putListFormJsonRow(data.id, input)
}
toast.push(
<Notification type="success">
{data.index === -1 ? 'Kayıt eklendi' : 'Kayıt güncellendi'}
</Notification>,
{ placement: 'top-end' },
)
handleClose()
} catch (error: any) {
toast.push(
<Notification type="danger">
Hata
<code>{error}</code>
</Notification>,
{ placement: 'top-end' },
)
} finally {
setSubmitting(false)
}
}}
>
{({ touched, errors, values, isSubmitting }) => (
<Form>
<FormContainer size="sm">
<div className="max-h-full overflow-y-auto p-2">
<FormItem
label="Field Name"
invalid={errors.fieldName && touched.fieldName}
errorMessage={errors.fieldName}
>
<Field name="fieldName">
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
componentAs={CreatableSelect}
field={field}
form={form}
isClearable={true}
options={fieldList}
value={fieldList.find(
(option: any) => option.value === values.fieldName,
)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
<FormItem
label="Caption"
invalid={errors.caption && touched.caption}
errorMessage={errors.caption}
>
<Field
type="text"
autoComplete="off"
name="caption"
placeholder="Caption"
component={Input}
/>
</FormItem>
<FormItem
label="Operator"
invalid={errors.operator && touched.operator}
errorMessage={errors.operator}
>
<Field name="operator">
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={cascadeFilterOperator}
value={cascadeFilterOperator.find(
(option: any) => option.value === values.operator,
)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
<FormItem
label="Default Value"
invalid={errors.defaultValue && touched.defaultValue}
errorMessage={errors.defaultValue}
>
<Field
type="text"
autoComplete="off"
name="defaultValue"
placeholder="Default Value"
component={Input}
/>
</FormItem>
<FormItem
label="Sql Query"
invalid={errors.sqlQuery && touched.sqlQuery}
errorMessage={errors.sqlQuery}
>
<Field
type="text"
autoComplete="off"
name="sqlQuery"
placeholder="Sql Query"
component={Input}
rows={6}
textArea={true}
/>
</FormItem>
</div>
<div className="text-right mt-4">
<Button className="ltr:mr-2 rtl:ml-2" variant="plain" onClick={handleClose}>
Cancel
</Button>
<Button variant="solid" loading={isSubmitting} type="submit">
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
</Button>
</div>
</FormContainer>
</Form>
)}
</Formik>
</>
)}
{data.operation === 'delete' && (
<>
<h5 className="mb-4">Delete</h5>
<p>Silmek istediğinize emin misiniz?</p>
<Formik
initialValues={data}
onSubmit={async (values, { setSubmitting }) => {
setSubmitting(true)
try {
await deleteListFormJsonRow(data.id, data.tabName, values.index)
toast.push(<Notification type="success">Kayıt silindi </Notification>, {
placement: 'top-end',
})
handleClose()
} catch (error: any) {
toast.push(
<Notification type="danger">
Hata
<code>{error}</code>
</Notification>,
{ placement: 'top-end' },
)
} finally {
setSubmitting(false)
}
// getListFormJsonRow()
}}
>
{({ isSubmitting }) => (
<Form>
<FormContainer size="sm">
<div className="text-right mt-4">
<Button className="ltr:mr-2 rtl:ml-2" variant="plain" onClick={handleClose}>
Cancel
</Button>
<Button variant="solid" loading={isSubmitting} type="submit">
{isSubmitting ? 'Deleting' : 'Delete'}
</Button>
</div>
</FormContainer>
</Form>
)}
</Formik>
</>
)}
</Dialog>
)
}
export default JsonRowOpDialogExtraFilter

View file

@ -2,6 +2,7 @@ import { ListFormEditTabs } from '@/proxy/admin/list-form/models'
import {
CommandColumnDto,
EditingFormDto,
ExtraFilterEditDto,
FieldsDefaultValueDto,
SubFormDto,
WidgetEditDto,
@ -20,4 +21,5 @@ export interface JsonRowDialogData {
commandValues?: CommandColumnDto
subFormValues?: SubFormDto
widgetValues?: WidgetEditDto
extraFilterValues?: ExtraFilterEditDto
}