Widget düzenlemesi

This commit is contained in:
Sedat Öztürk 2025-11-09 00:19:00 +03:00
parent 4d9ae1563d
commit 533e937307
10 changed files with 127 additions and 39 deletions

View file

@ -13,4 +13,5 @@ public class WidgetEditDto
public string SubTitle { get; set; } public string SubTitle { get; set; }
public string OnClick { get; set; } public string OnClick { get; set; }
public string ClassName { get; set; } public string ClassName { get; set; }
public bool IsActive { get; set; }
} }

View file

@ -6,6 +6,7 @@ public class WidgetDto
public int ColSpan { get; set; } public int ColSpan { get; set; }
public string ClassName { get; set; } public string ClassName { get; set; }
public List<WidgetItemDto> Items { get; set; } public List<WidgetItemDto> Items { get; set; }
public bool IsActive { get; set; }
} }
public class WidgetItemDto public class WidgetItemDto

View file

@ -231,7 +231,7 @@ public class ListFormSelectAppService : PlatformAppService, IListFormSelectAppSe
if (!listForm.WidgetsJson.IsNullOrWhiteSpace()) if (!listForm.WidgetsJson.IsNullOrWhiteSpace())
{ {
var widgetList = JsonSerializer.Deserialize<WidgetEditDto[]>(listForm.WidgetsJson) ?? []; var widgetList = JsonSerializer.Deserialize<WidgetEditDto[]>(listForm.WidgetsJson) ?? [];
foreach (var widget in widgetList) foreach (var widget in widgetList.Where(w => w.IsActive))
{ {
if (!string.IsNullOrWhiteSpace(widget.SqlQuery)) if (!string.IsNullOrWhiteSpace(widget.SqlQuery))
{ {

View file

@ -4957,6 +4957,12 @@
"en": "Value Class Name", "en": "Value Class Name",
"tr": "Değer Sütun Sınıf Adı" "tr": "Değer Sütun Sınıf Adı"
}, },
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.WidgetStatus",
"en": "Status",
"tr": "Durum"
},
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "ListForms.ListFormEdit.WidgetColor", "key": "ListForms.ListFormEdit.WidgetColor",

View file

@ -16,6 +16,7 @@ public class Widget : ValueObject
public string SubTitle { get; set; } public string SubTitle { get; set; }
public string OnClick { get; set; } public string OnClick { get; set; }
public string ClassName { get; set; } public string ClassName { get; set; }
public bool IsActive { get; set; }
protected override IEnumerable<object> GetAtomicValues() protected override IEnumerable<object> GetAtomicValues()
{ {
@ -30,5 +31,6 @@ public class Widget : ValueObject
yield return SubTitle; yield return SubTitle;
yield return OnClick; yield return OnClick;
yield return ClassName; yield return ClassName;
yield return IsActive;
} }
} }

View file

@ -792,6 +792,7 @@ export interface WidgetGroupDto {
colSpan?: number colSpan?: number
className?: string className?: string
items: WidgetEditDto[] items: WidgetEditDto[]
isActive: boolean
} }
export interface WidgetEditDto { export interface WidgetEditDto {
@ -806,6 +807,7 @@ export interface WidgetEditDto {
icon: string icon: string
subTitle: string subTitle: string
onClick: string onClick: string
isActive: boolean
} }
export interface LayoutDto { export interface LayoutDto {

View file

@ -59,6 +59,7 @@ function FormTabWidgets(props: { listFormCode: string }) {
<Th>{translate('::ListForms.ListFormEdit.WidgetSqlQuery')}</Th> <Th>{translate('::ListForms.ListFormEdit.WidgetSqlQuery')}</Th>
<Th>{translate('::ListForms.ListFormEdit.WidgetClassName')}</Th> <Th>{translate('::ListForms.ListFormEdit.WidgetClassName')}</Th>
<Th>{translate('::ListForms.ListFormEdit.WidgetValueClassName')}</Th> <Th>{translate('::ListForms.ListFormEdit.WidgetValueClassName')}</Th>
<Th>{translate('::ListForms.ListFormEdit.WidgetStatus')}</Th>
</Tr> </Tr>
</THead> </THead>
<TBody> <TBody>
@ -108,6 +109,7 @@ function FormTabWidgets(props: { listFormCode: string }) {
<Td>{row.sqlQuery}</Td> <Td>{row.sqlQuery}</Td>
<Td>{row.className}</Td> <Td>{row.className}</Td>
<Td>{row.valueClassName}</Td> <Td>{row.valueClassName}</Td>
<Td>{row.isActive ? 'Active' : 'Inactive'}</Td>
</Tr> </Tr>
))} ))}
</TBody> </TBody>

View file

@ -1,5 +1,6 @@
import { import {
Button, Button,
Checkbox,
Dialog, Dialog,
FormContainer, FormContainer,
FormItem, FormItem,
@ -97,6 +98,7 @@ function JsonRowOpDialogWidget({
icon: 'Icon', icon: 'Icon',
subTitle: 'SubTitle', subTitle: 'SubTitle',
onClick: '', onClick: '',
isActive: false,
} }
} }
validationSchema={schema} validationSchema={schema}
@ -137,40 +139,55 @@ function JsonRowOpDialogWidget({
<Form> <Form>
<FormContainer size="sm"> <FormContainer size="sm">
<div className="h-full overflow-y-auto p-2"> <div className="h-full overflow-y-auto p-2">
<FormItem <div className="grid grid-cols-3 gap-4">
label="Column Gap (Sütun Boşluğu)" <FormItem
invalid={errors.colGap && touched.colGap} label="Column Gap (Sütun Boşluğu)"
errorMessage={errors.colGap} invalid={errors.colGap && touched.colGap}
> errorMessage={errors.colGap}
<Field >
type="number" <Field
autoComplete="off" type="number"
name="colGap" autoComplete="off"
placeholder="Column Gap" name="colGap"
component={Input} placeholder="Column Gap"
/> component={Input}
</FormItem> />
</FormItem>
<FormItem <FormItem
label="Column Span (Sütun Genişliği)" label="Column Span (Sütun Genişliği)"
invalid={errors.colSpan && touched.colSpan} invalid={errors.colSpan && touched.colSpan}
errorMessage={errors.colSpan} errorMessage={errors.colSpan}
> >
<Field type="text" autoComplete="off" name="colSpan" placeholder="colSpan"> <Field type="text" autoComplete="off" name="colSpan" placeholder="colSpan">
{({ field, form }: FieldProps<SelectBoxOption>) => ( {({ field, form }: FieldProps<SelectBoxOption>) => (
<Select <Select
field={field} field={field}
form={form} form={form}
isClearable={true} isClearable={true}
options={colSpanOptions} options={colSpanOptions}
value={colSpanOptions?.filter( value={colSpanOptions?.filter(
(option: any) => option.value === values.colSpan, (option: any) => option.value === values.colSpan,
)} )}
onChange={(option) => form.setFieldValue(field.name, option?.value)} onChange={(option) => form.setFieldValue(field.name, option?.value)}
/> />
)} )}
</Field> </Field>
</FormItem> </FormItem>
<FormItem
label="IsActive"
invalid={errors.isActive && touched.isActive}
errorMessage={errors.isActive}
>
<Field
autoComplete="off"
name="isActive"
placeholder="IsActive"
component={Checkbox}
/>
</FormItem>
</div>
<FormItem <FormItem
label="Sql Query" label="Sql Query"
@ -183,7 +200,6 @@ function JsonRowOpDialogWidget({
name="sqlQuery" name="sqlQuery"
placeholder="Sql Query" placeholder="Sql Query"
component={Input} component={Input}
rows={6}
textArea={true} textArea={true}
/> />
</FormItem> </FormItem>

View file

@ -87,6 +87,7 @@ const Grid = (props: GridProps) => {
const gridRef = useRef<DataGrid>() const gridRef = useRef<DataGrid>()
const refListFormCode = useRef('') const refListFormCode = useRef('')
const widgetGroupRef = useRef<HTMLDivElement>(null)
const [gridDataSource, setGridDataSource] = useState<CustomStore<any, any>>() const [gridDataSource, setGridDataSource] = useState<CustomStore<any, any>>()
const [columnData, setColumnData] = useState<GridColumnData[]>() const [columnData, setColumnData] = useState<GridColumnData[]>()
@ -95,6 +96,7 @@ const Grid = (props: GridProps) => {
const [extraFilters, setExtraFilters] = useState<GridExtraFilterState[]>([]) const [extraFilters, setExtraFilters] = useState<GridExtraFilterState[]>([])
const [gridDto, setGridDto] = useState<GridDto>() const [gridDto, setGridDto] = useState<GridDto>()
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false) const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
const [widgetGroupHeight, setWidgetGroupHeight] = useState(0)
const preloadExportLibs = () => { const preloadExportLibs = () => {
import('exceljs') import('exceljs')
@ -593,6 +595,29 @@ const Grid = (props: GridProps) => {
gridRef.current.instance.option('stateStoring', stateStoring) gridRef.current.instance.option('stateStoring', stateStoring)
}, [columnData]) }, [columnData])
// WidgetGroup yüksekliğini hesapla
useEffect(() => {
const calculateWidgetHeight = () => {
if (widgetGroupRef.current) {
const height = widgetGroupRef.current.offsetHeight
setWidgetGroupHeight(height)
}
}
// İlk render'da hesapla
calculateWidgetHeight()
// Resize durumunda tekrar hesapla
const resizeObserver = new ResizeObserver(calculateWidgetHeight)
if (widgetGroupRef.current) {
resizeObserver.observe(widgetGroupRef.current)
}
return () => {
resizeObserver.disconnect()
}
}, [gridDto?.widgets])
const onExporting = async (e: DataGridTypes.ExportingEvent) => { const onExporting = async (e: DataGridTypes.ExportingEvent) => {
// DevExtremein varsayılan export davranışını iptal ediyoruz; kendi akışımızı çalıştıracağız // DevExtremein varsayılan export davranışını iptal ediyoruz; kendi akışımızı çalıştıracağız
e.cancel = true e.cancel = true
@ -663,7 +688,9 @@ const Grid = (props: GridProps) => {
return ( return (
<> <>
<WidgetGroup widgetGroups={gridDto?.widgets ?? []} /> <div ref={widgetGroupRef}>
<WidgetGroup widgetGroups={gridDto?.widgets ?? []} />
</div>
<Container className={DX_CLASSNAMES}> <Container className={DX_CLASSNAMES}>
{!isSubForm && ( {!isSubForm && (
@ -682,7 +709,10 @@ const Grid = (props: GridProps) => {
//dataSource={gridDataSource} //dataSource={gridDataSource}
//remoteOperations={{ groupPaging: true }} //remoteOperations={{ groupPaging: true }}
//remoteOperations={false} //remoteOperations={false}
height={gridDto.gridOptions.height || 'calc(100vh - 150px)'} height={
gridDto.gridOptions.height ||
`calc(100vh - ${170 + widgetGroupHeight}px)`
}
width={gridDto.gridOptions.width || '100%'} width={gridDto.gridOptions.width || '100%'}
allowColumnResizing={gridDto.gridOptions.columnOptionDto?.allowColumnResizing} allowColumnResizing={gridDto.gridOptions.columnOptionDto?.allowColumnResizing}
allowColumnReordering={gridDto.gridOptions.columnOptionDto?.allowColumnReordering} allowColumnReordering={gridDto.gridOptions.columnOptionDto?.allowColumnReordering}

View file

@ -80,6 +80,7 @@ const Tree = (props: TreeProps) => {
const gridRef = useRef<TreeListDx>() const gridRef = useRef<TreeListDx>()
const refListFormCode = useRef('') const refListFormCode = useRef('')
const widgetGroupRef = useRef<HTMLDivElement>(null)
const [treeListDataSource, setTreeListDataSource] = useState<CustomStore<any, any>>() const [treeListDataSource, setTreeListDataSource] = useState<CustomStore<any, any>>()
const [columnData, setColumnData] = useState<GridColumnData[]>() const [columnData, setColumnData] = useState<GridColumnData[]>()
@ -88,6 +89,8 @@ const Tree = (props: TreeProps) => {
const [extraFilters, setExtraFilters] = useState<GridExtraFilterState[]>([]) const [extraFilters, setExtraFilters] = useState<GridExtraFilterState[]>([])
const [gridDto, setGridDto] = useState<GridDto>() const [gridDto, setGridDto] = useState<GridDto>()
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false) const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
const [widgetGroupHeight, setWidgetGroupHeight] = useState(0)
const [expandedRowKeys, setExpandedRowKeys] = useState<any[]>([]) const [expandedRowKeys, setExpandedRowKeys] = useState<any[]>([])
const preloadExportLibs = () => { const preloadExportLibs = () => {
@ -435,6 +438,29 @@ const Tree = (props: TreeProps) => {
} }
}, [listFormCode]) }, [listFormCode])
// WidgetGroup yüksekliğini hesapla
useEffect(() => {
const calculateWidgetHeight = () => {
if (widgetGroupRef.current) {
const height = widgetGroupRef.current.offsetHeight
setWidgetGroupHeight(height)
}
}
// İlk render'da hesapla
calculateWidgetHeight()
// Resize durumunda tekrar hesapla
const resizeObserver = new ResizeObserver(calculateWidgetHeight)
if (widgetGroupRef.current) {
resizeObserver.observe(widgetGroupRef.current)
}
return () => {
resizeObserver.disconnect()
}
}, [gridDto?.widgets])
useEffect(() => { useEffect(() => {
if (!gridDto) { if (!gridDto) {
return return
@ -565,7 +591,9 @@ const Tree = (props: TreeProps) => {
return ( return (
<> <>
<WidgetGroup widgetGroups={gridDto?.widgets ?? []} /> <div ref={widgetGroupRef}>
<WidgetGroup widgetGroups={gridDto?.widgets ?? []} />
</div>
<Container className={DX_CLASSNAMES}> <Container className={DX_CLASSNAMES}>
{!isSubForm && ( {!isSubForm && (
@ -580,7 +608,7 @@ const Tree = (props: TreeProps) => {
<TreeListDx <TreeListDx
ref={gridRef as any} ref={gridRef as any}
id={'TreeList-' + listFormCode} id={'TreeList-' + listFormCode}
height={gridDto.gridOptions.height || 'calc(100vh - 150px)'} height={gridDto.gridOptions.height || `calc(100vh - ${170 + widgetGroupHeight}px)`}
width={gridDto.gridOptions.width || '100%'} width={gridDto.gridOptions.width || '100%'}
dataStructure="plain" dataStructure="plain"
keyExpr={gridDto.gridOptions.treeOptionDto?.keyExpr} keyExpr={gridDto.gridOptions.treeOptionDto?.keyExpr}