Listeleri mükemmelleştirme ikon vs.

This commit is contained in:
Sedat ÖZTÜRK 2025-09-29 11:33:51 +03:00
parent 9ea283712e
commit 1d36fc8225
7 changed files with 151 additions and 27 deletions

View file

@ -22763,7 +22763,7 @@
"accountHolder": "Özlem Öztürk",
"branch": "03663 / Enpara",
"accountNumber": "73941177",
"iban": "TR65 0011 1000 0000 0073 9411 77"
"iban": "TR11 0015 7000 0000 0073 9411 77"
},
"workHour": {
"weekday": "Public.contact.workHours.weekday",

View file

@ -806,6 +806,7 @@ public class SelectQueryManager : PlatformDomainService, ISelectQueryManager
var sql = $@"
SELECT {string.Join(", ", selectParts)}
FROM [{listform.SelectCommand}]
{GetWhereString()}
GROUP BY {argumentExpression}";
return sql;

View file

@ -288,11 +288,23 @@ const useListFormColumns = ({
}
if (hasUpdate) {
column.buttons.push('edit')
// column.buttons.push('edit')
column.buttons.push({
icon: 'edit',
hint: translate('::Edit'),
name: 'edit',
text: translate('::Edit'),
})
}
if (hasDelete) {
column.buttons.push('delete')
// column.buttons.push('delete')
column.buttons.push({
icon: 'trash',
hint: translate('::Delete'),
name: 'delete',
text: translate('::Delete'),
})
}
gridDto.gridOptions.commandColumnDto.forEach((action) => {

View file

@ -4,12 +4,12 @@ import { captionize } from 'devextreme/core/utils/inflector'
import { useListFormCustomDataSource } from '@/shared/useListFormCustomDataSource'
import { Button, Pagination, Select } from '@/components/ui'
import classNames from 'classnames'
import { FaSearch } from 'react-icons/fa'
import { FaCog, FaSearch } from 'react-icons/fa'
import FormDevExpress from '../form/FormDevExpress'
import { GroupItem } from 'devextreme/ui/form'
import { Form as FormDx } from 'devextreme-react/form'
import FormButtons from '../form/FormButtons'
import { Link, useNavigate } from 'react-router-dom'
import { useNavigate } from 'react-router-dom'
import { ROUTES_ENUM } from '@/routes/route.constant'
import CustomStore from 'devextreme/data/custom_store'
import { PermissionResults, SimpleItemWithColData } from '../form/types'
@ -20,6 +20,7 @@ import { Container, Loading } from '@/components/shared'
import WidgetGroup from '@/components/common/WidgetGroup'
import { GridExtraFilterState } from './Utils'
import { useStoreActions, useStoreState } from '@/store/store'
import { usePWA } from '@/utils/hooks/usePWA'
const CardItem = ({
isSubForm,
@ -41,8 +42,9 @@ const CardItem = ({
const [formData, setFormData] = useState(row)
const refForm = useRef<FormDx>(null)
const navigate = useNavigate()
const { checkPermission } = usePermission()
const { translate } = useLocalization()
const { checkPermission } = usePermission()
const isPwaMode = usePWA()
const keyField = gridDto.gridOptions.keyFieldName
const rowId = row[keyField!]
@ -204,6 +206,8 @@ const Card = (props: CardProps) => {
const [searchText, setSearchText] = useState('')
const [prevValue, setPrevValue] = useState('')
const [loading, setLoading] = useState(false)
const { checkPermission } = usePermission()
const isPwaMode = usePWA()
const [extraFilters, setExtraFilters] = useState<GridExtraFilterState[]>([])
const { states } = useStoreState((state) => state.base.lists)
@ -355,7 +359,7 @@ const Card = (props: CardProps) => {
<WidgetGroup widgetGroups={gridDto.widgets || []} />
<Container>
<div className="p-1 bg-white dark:bg-neutral-800 dark:border-neutral-700 ">
<div className="bg-white dark:bg-neutral-800 dark:border-neutral-700 ">
<div className="flex justify-end items-center">
<div className="relative py-1 flex gap-1 border-b-1">
<FaSearch className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400 text-sm" />
@ -444,6 +448,26 @@ const Card = (props: CardProps) => {
>
5
</Button>
{checkPermission(gridDto?.gridOptions.permissionDto.u) && (
<Button
size="xs"
variant={'default'}
className="text-sm"
onClick={() => {
window.open(
ROUTES_ENUM.protected.saas.listFormManagement.edit.replace(
':listFormCode',
listFormCode,
),
isPwaMode ? '_self' : '_blank',
)
}}
title="Form Manager"
>
<FaCog className="w-3 h-3" />
</Button>
)}
</div>
</div>

View file

@ -4,7 +4,7 @@ import { Container } from '@/components/shared'
import { DX_CLASSNAMES } from '@/constants/app.constant'
import { useLocalization } from '@/utils/hooks/useLocalization'
import DxChart from 'devextreme-react/chart'
import { useEffect, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { Helmet } from 'react-helmet'
import { useParams, useSearchParams } from 'react-router-dom'
import { useListFormCustomDataSource } from '@/shared/useListFormCustomDataSource'
@ -13,7 +13,7 @@ import { usePermission } from '@/utils/hooks/usePermission'
import { Button } from '@/components/ui'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { usePWA } from '@/utils/hooks/usePWA'
import { FaInfoCircle, FaSyncAlt } from 'react-icons/fa'
import { FaCog, FaSearch, FaSyncAlt } from 'react-icons/fa'
import { buildSeriesDto } from './Utils'
interface ChartProps extends CommonProps, Meta {
@ -39,13 +39,19 @@ const Chart = (props: ChartProps) => {
const params = useParams()
const _listFormCode = props?.listFormCode ?? params?.listFormCode ?? ''
const [searchText, setSearchText] = useState('')
const [prevValue, setPrevValue] = useState('')
const [urlSearchParams, setUrlSearchParams] = useState<URLSearchParams>(
searchParams ? new URLSearchParams(searchParams) : new URLSearchParams(),
)
useEffect(() => {
if (!gridDto) return
const dataSource = createSelectDataSource(
gridDto.gridOptions,
listFormCode,
searchParams,
urlSearchParams,
[],
true,
)
@ -94,7 +100,53 @@ const Chart = (props: ChartProps) => {
}
setChartOptions(options)
}, [gridDto, searchParams])
}, [gridDto, searchParams, urlSearchParams])
const onFilter = useCallback(
(value?: string) => {
const text = value !== undefined ? value.trim() : searchText.trim()
if (!gridDto?.columnFormats) return
const newParams = new URLSearchParams(urlSearchParams.toString())
if (!text) {
newParams.delete('filter')
setUrlSearchParams(newParams)
return
}
const merged = gridDto.columnFormats
.filter(
(col) =>
col.dataType === 'string' &&
col.visible &&
col.width &&
col.allowSearch &&
col.width > 0,
)
.map((col) => [col.fieldName, 'contains', text])
let filter: any = null
if (merged.length === 1) {
filter = merged[0]
} else if (merged.length > 1) {
filter = merged.reduce((acc, f, idx) => {
if (idx === 0) return f
return [acc, 'or', f]
}, null as any)
}
if (filter) {
newParams.set('filter', JSON.stringify(filter))
} else {
newParams.delete('filter')
}
setUrlSearchParams(newParams)
},
[gridDto, urlSearchParams, searchText],
)
return (
<Container className={DX_CLASSNAMES}>
@ -109,6 +161,32 @@ const Chart = (props: ChartProps) => {
<div className="p-1 bg-white dark:bg-neutral-800 dark:border-neutral-700 h-full">
<div className="flex justify-end items-center h-full">
<div className="relative pb-1 flex gap-1 border-b-1">
<FaSearch className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400 text-sm" />
<input
type="text"
placeholder="Search..."
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
onFilter(e.currentTarget.value)
setPrevValue(e.currentTarget.value.trim()) // Enter ile tetiklenirse güncelle
}
}}
onBlur={(e) => {
const newValue = e.currentTarget.value.trim()
// 1. Değer değişmemişse => hiçbir şey yapma
if (newValue === prevValue) return
// 2. Yeni değer boş, ama eskiden değer vardı => filtre temizle
// 3. Yeni değer dolu ve eskisinden farklı => filtre uygula
onFilter(newValue)
setPrevValue(newValue)
}}
className="p-1 pl-6 pr-2 border border-1 outline-none text-xs text-gray-700 dark:text-gray-200 placeholder-gray-400 rounded"
/>
<Button
size="xs"
variant={'default'}
@ -116,7 +194,7 @@ const Chart = (props: ChartProps) => {
onClick={async () => {
await props.refreshGridDto()
}}
title="Reset Grid State"
title="Refresh Data"
>
<FaSyncAlt className="w-3 h-3" />
</Button>
@ -136,7 +214,7 @@ const Chart = (props: ChartProps) => {
}}
title="Form Manager"
>
<FaInfoCircle className="w-3 h-3" />
<FaCog className="w-3 h-3" />
</Button>
)}
</div>

View file

@ -34,7 +34,15 @@ import {
import { useFilters } from './useFilters'
import WidgetGroup from '@/components/common/WidgetGroup'
import { Button } from '@/components/ui'
import { FaInfoCircle, FaSyncAlt, FaTrash, FaTrashAlt } from 'react-icons/fa'
import {
FaCog,
FaInfoCircle,
FaSyncAlt,
FaTimes,
FaTrash,
FaTrashAlt,
FaUndo,
} from 'react-icons/fa'
import { usePermission } from '@/utils/hooks/usePermission'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { usePWA } from '@/utils/hooks/usePWA'
@ -317,7 +325,7 @@ const Pivot = (props: PivotProps) => {
onClick={clearPivotFilters}
title="Remove Filter"
>
<FaTrash className="w-3 h-3" />
<FaTimes className="w-3 h-3" />
</Button>
<Button
@ -327,7 +335,7 @@ const Pivot = (props: PivotProps) => {
onClick={resetPivotGridState}
title="Reset Grid State"
>
<FaSyncAlt className="w-3 h-3" />
<FaUndo className="w-3 h-3" />
</Button>
{checkPermission(gridDto?.gridOptions.permissionDto.u) && (
@ -346,7 +354,7 @@ const Pivot = (props: PivotProps) => {
}}
title="Form Manager"
>
<FaInfoCircle className="w-3 h-3" />
<FaCog className="w-3 h-3" />
</Button>
)}
</div>

View file

@ -129,15 +129,7 @@ const useFilters = ({
menus.push({
text: translate('::ListForms.ListForm.ResetGridState'),
id: 'resetGridState',
icon: 'refresh',
})
}
if (checkPermission(gridDto?.gridOptions.permissionDto.u)) {
menus.push({
text: translate('::ListForms.ListForm.Manage'),
id: 'openManage',
icon: 'preferences',
icon: 'revert',
})
}
@ -149,6 +141,14 @@ const useFilters = ({
})
}
if (checkPermission(gridDto?.gridOptions.permissionDto.u)) {
menus.push({
text: translate('::ListForms.ListForm.Manage'),
id: 'openManage',
icon: 'preferences',
})
}
if (checkPermission(gridDto?.gridOptions.permissionDto.e)) {
items.push({
locateInMenu: 'auto',
@ -228,6 +228,7 @@ const useFilters = ({
{
text: translate('::ListForms.ListForm.GridMenu'),
items: menus,
icon: 'overflow',
},
],
},