List teki Card üzeirnden değişiklikler

FormDevExpress kullanıldı. Tüm yetenekleriyle birlikte
This commit is contained in:
Sedat Öztürk 2025-09-21 17:42:24 +03:00
parent e22ea52c6a
commit 4dc8ac1e47
8 changed files with 183 additions and 206 deletions

View file

@ -16,6 +16,7 @@ import { GridExtraFilterState } from '../list/Utils'
import { usePWA } from '@/utils/hooks/usePWA' import { usePWA } from '@/utils/hooks/usePWA'
const FormButtons = (props: { const FormButtons = (props: {
isSubForm?: boolean
mode: RowMode mode: RowMode
listFormCode: string listFormCode: string
id?: string id?: string
@ -33,6 +34,7 @@ const FormButtons = (props: {
onActionView?: () => void onActionView?: () => void
}) => { }) => {
const { const {
isSubForm,
mode, mode,
listFormCode, listFormCode,
id, id,
@ -168,21 +170,24 @@ const FormButtons = (props: {
{!!commandColumnData?.buttons?.filter((item) => typeof item !== 'string').length && ( {!!commandColumnData?.buttons?.filter((item) => typeof item !== 'string').length && (
<Badge innerClass="bg-blue-500" /> <Badge innerClass="bg-blue-500" />
)} )}
<Button
variant="solid" {!isSubForm && (
size="xs" <Button
color="gray-500" variant="solid"
title={translate('::Cancel')} size="xs"
onClick={() => { color="gray-500"
if (onActionView && id) { title={translate('::Cancel')}
onActionView() onClick={() => {
} else { if (onActionView && id) {
navigate(-1) onActionView()
} } else {
}} navigate(-1)
> }
<FaBackward /> }}
</Button> >
<FaBackward />
</Button>
)}
{mode != 'new' && ( {mode != 'new' && (
<Button <Button
variant="solid" variant="solid"
@ -267,7 +272,7 @@ const FormButtons = (props: {
<FaSave /> <FaSave />
</Button> </Button>
)} )}
{checkPermission(gridDto?.gridOptions.permissionDto.c) && ( {checkPermission(gridDto?.gridOptions.permissionDto.c) && !isSubForm && (
<Button <Button
variant="solid" variant="solid"
color="green-500" color="green-500"

View file

@ -66,10 +66,11 @@ const FormEdit = (
defaultTitle="Sözsoft Kurs Platform" defaultTitle="Sözsoft Kurs Platform"
></Helmet> ></Helmet>
)} )}
<div className="flex items-center justify-between mb-2"> <div className={`flex items-center mb-2 ${isSubForm ? 'justify-center' : 'justify-between'}`}>
<h3>{translate('::' + gridDto?.gridOptions.title)}</h3> {!isSubForm && <h3>{translate('::' + gridDto?.gridOptions.title)}</h3>}
{permissionResults && ( {permissionResults && (
<FormButtons <FormButtons
isSubForm={isSubForm}
mode={mode} mode={mode}
listFormCode={listFormCode} listFormCode={listFormCode}
id={formData?.Id} id={formData?.Id}
@ -94,7 +95,6 @@ const FormEdit = (
formItems={formItems} formItems={formItems}
setFormData={setFormData} setFormData={setFormData}
/> />
<hr className="my-2" />
<SubForms gridDto={gridDto!} formData={formData} level={level ?? 0} /> <SubForms gridDto={gridDto!} formData={formData} level={level ?? 0} />
</Container> </Container>
) )

View file

@ -152,12 +152,11 @@ const FormNew = (
defaultTitle="Sözsoft Kurs Platform" defaultTitle="Sözsoft Kurs Platform"
></Helmet> ></Helmet>
)} )}
<div className="flex items-center justify-between mb-4"> <div className={`flex items-center mb-2 ${isSubForm ? 'justify-center' : 'justify-between'}`}>
<h3> {!isSubForm && <h3>{translate('::' + gridDto?.gridOptions.title)}</h3>}
{translate('::AddNew')} {translate('::App.Languages.Language')}
</h3>
{permissionResults && ( {permissionResults && (
<FormButtons <FormButtons
isSubForm={isSubForm}
mode={mode} mode={mode}
listFormCode={listFormCode} listFormCode={listFormCode}
id={formData?.Id} id={formData?.Id}

View file

@ -61,10 +61,11 @@ const FormView = (
defaultTitle="Sözsoft Kurs Platform" defaultTitle="Sözsoft Kurs Platform"
></Helmet> ></Helmet>
)} )}
<div className="flex items-center justify-between mb-2"> <div className={`flex items-center mb-2 ${isSubForm ? 'justify-center' : 'justify-between'}`}>
<h3>{translate('::' + gridDto?.gridOptions.title)}</h3> {!isSubForm && <h3>{translate('::' + gridDto?.gridOptions.title)}</h3>}
{permissionResults && ( {permissionResults && (
<FormButtons <FormButtons
isSubForm={isSubForm}
mode={mode} mode={mode}
listFormCode={listFormCode} listFormCode={listFormCode}
id={formData?.Id} id={formData?.Id}
@ -89,7 +90,6 @@ const FormView = (
formItems={formItems} formItems={formItems}
setFormData={() => {}} setFormData={() => {}}
/> />
<hr className="my-2" />
<SubForms gridDto={gridDto!} formData={formData} level={level ?? 0} refreshData={fetchData} /> <SubForms gridDto={gridDto!} formData={formData} level={level ?? 0} refreshData={fetchData} />
</Container> </Container>
) )

View file

@ -78,6 +78,7 @@ const SubForms = (props: {
} }
} }
}} }}
className='mt-2'
> >
<TabList> <TabList>
{subForms.map((subForm, i) => { {subForms.map((subForm, i) => {

149
ui/src/views/list/Card.tsx Normal file
View file

@ -0,0 +1,149 @@
import { useCallback, useEffect, useState } from 'react'
import { GridDto } from '@/proxy/form/models'
import { useListFormCustomDataSource } from '@/shared/useListFormCustomDataSource'
import { Pagination, Select } from '@/components/ui'
import classNames from 'classnames'
import { getList } from '@/services/form.service'
import { FaInbox } from 'react-icons/fa'
import FormView from '../form/FormView'
interface CardProps {
listFormCode: string
searchParams?: URLSearchParams
}
type Option = {
value: number
label: string
}
const CardItem = ({
row,
gridDto,
listFormCode,
}: {
row: any
gridDto: GridDto
listFormCode: string
}) => {
const keyField = gridDto.gridOptions.keyFieldName
const rowId = row[keyField!]
return (
<div className="bg-white dark:bg-neutral-800 border dark:border-neutral-700 flex flex-col p-4">
<div className="flex-grow">
<FormView listFormCode={listFormCode} id={rowId} isSubForm={true} />
</div>
</div>
)
}
const Card = ({ listFormCode, searchParams }: CardProps) => {
const { createSelectDataSource } = useListFormCustomDataSource({})
const [gridDto, setGridDto] = useState<GridDto>()
const [data, setData] = useState<any[]>([])
const [totalCount, setTotalCount] = useState(0)
const [currentPage, setCurrentPage] = useState(1)
const [pageSize, setPageSize] = useState(20)
const [pageSizeOptions, setPageSizeOptions] = useState<Option[]>([])
const onPageSizeSelect = ({ value }: Option) => {
setPageSize(value)
setCurrentPage(1)
}
const onPageChange = (page: number) => {
setCurrentPage(page)
}
const loadData = useCallback(() => {
if (!gridDto) return
const store = createSelectDataSource(gridDto.gridOptions, listFormCode, searchParams)
const loadOptions = {
skip: (currentPage - 1) * pageSize,
take: pageSize,
requireTotalCount: true,
}
store.load(loadOptions).then((res: any) => {
setData(res.data)
setTotalCount(res.totalCount || 0)
})
}, [gridDto, listFormCode, searchParams, currentPage, pageSize])
useEffect(() => {
getList({ listFormCode }).then((res: any) => setGridDto(res.data))
}, [listFormCode])
useEffect(() => {
if (!gridDto) return
const pagerOptions = gridDto.gridOptions.pagerOptionDto
const allowedSizes =
pagerOptions?.allowedPageSizes
?.split(',')
.map((s) => Number(s.trim()))
.filter((n) => !isNaN(n) && n > 0) || [20, 50, 100]
setPageSizeOptions(allowedSizes.map((size) => ({ value: size, label: `${size} page` })))
}, [gridDto, listFormCode, searchParams])
useEffect(() => {
loadData()
}, [loadData])
if (!gridDto) return null
return (
<>
{data.length === 0 && (
<div className="flex flex-col items-center justify-center p-10 bg-gray-50 dark:bg-neutral-800/50 rounded-md border-2 border-dashed border-gray-200 dark:border-neutral-700">
<div className="text-center">
<FaInbox className="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500" />
<p className="mt-4 text-lg font-semibold text-gray-700 dark:text-gray-300">
Kayıt Bulunamadı
</p>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
Görüntülenecek herhangi bir veri yok.
</p>
</div>
</div>
)}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-4 gap-4">
{data.map((row, idx) => {
const keyField = gridDto.gridOptions.keyFieldName
const rowId = row[keyField!]
return <CardItem key={rowId || idx} row={row} gridDto={gridDto} listFormCode={listFormCode} />
})}
</div>
{gridDto.gridOptions.pagerOptionDto?.visible && totalCount > pageSize && (
<div className={classNames('flex items-center justify-between border-t-1 gap-4 mt-4')}>
<div className="flex items-center gap-2">
<span className="text-xs text-gray-600 dark:text-gray-300">
Toplam {totalCount} kayıt
</span>
<Select
size="xs"
menuPlacement="top"
value={pageSizeOptions.find((o) => o.value === pageSize)}
options={pageSizeOptions}
onChange={(selected) => onPageSizeSelect(selected as Option)}
/>
</div>
<Pagination
currentPage={currentPage}
total={totalCount}
pageSize={pageSize}
onChange={onPageChange}
/>
</div>
)}
</>
)
}
export default Card

View file

@ -1,176 +0,0 @@
import React, { useCallback, useEffect, useState } from 'react'
import { GridDto } from '@/proxy/form/models'
import { captionize } from 'devextreme/core/utils/inflector'
import { useListFormCustomDataSource } from '@/shared/useListFormCustomDataSource'
import { useNavigate } from 'react-router-dom'
import { Pagination, Select } from '@/components/ui'
import classNames from 'classnames'
import { useStoreState } from '@/store/store'
import { getList } from '@/services/form.service'
import { FaAngleRight, FaInbox } from 'react-icons/fa'
interface MultiFormViewProps {
listFormCode: string
searchParams?: URLSearchParams
}
const CardView = ({ listFormCode, searchParams }: MultiFormViewProps) => {
const { createSelectDataSource } = useListFormCustomDataSource({})
const [gridDto, setGridDto] = useState<GridDto>()
const navigate = useNavigate()
const [data, setData] = useState<any[]>([])
const [totalCount, setTotalCount] = useState(0)
const [currentPage, setCurrentPage] = useState(1)
const [pageSize, setPageSize] = useState(10)
const [pageSizeOptions, setPageSizeOptions] = useState<Option[]>([])
const mode = useStoreState((state) => state.theme.mode)
type Option = {
value: number
label: string
}
const onPageSizeSelect = ({ value }: Option) => {
setPageSize(value)
setCurrentPage(1) // Sayfa boyutu değiştiğinde ilk sayfaya dön
}
const onPageChange = (page: number) => {
setCurrentPage(page)
}
const loadData = useCallback(() => {
if (!gridDto) return
const store = createSelectDataSource(gridDto.gridOptions, listFormCode, searchParams)
const loadOptions = {
skip: (currentPage - 1) * pageSize,
take: pageSize,
requireTotalCount: true,
}
store.load(loadOptions).then((res: any) => {
setData(res.data)
setTotalCount(res.totalCount || 0)
})
}, [gridDto, listFormCode, searchParams, currentPage, pageSize])
useEffect(() => {
getList({ listFormCode }).then((res: any) => setGridDto(res.data))
}, [listFormCode])
useEffect(() => {
if (!gridDto) return
const pagerOptions = gridDto.gridOptions.pagerOptionDto
// const initialPageSize = gridDto.gridOptions.pageSize || 10
const allowedSizes = pagerOptions?.allowedPageSizes
?.split(',')
.map((s) => Number(s.trim()))
.filter((n) => !isNaN(n) && n > 0) || [20, 50, 100]
// setPageSize(initialPageSize)
setPageSizeOptions(allowedSizes.map((size) => ({ value: size, label: `${size} page` })))
}, [gridDto, listFormCode, searchParams])
useEffect(() => {
loadData()
}, [loadData])
if (!gridDto) return null
return (
<>
{data.length === 0 && (
<div className="flex flex-col items-center justify-center p-10 bg-gray-50 dark:bg-neutral-800/50 rounded-md border-2 border-dashed border-gray-200 dark:border-neutral-700">
<div className="text-center">
<FaInbox className="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500" />
<p className="mt-4 text-lg font-semibold text-gray-700 dark:text-gray-300">Kayıt Bulunamadı</p>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">Görüntülenecek herhangi bir veri yok.</p>
</div>
</div>
)}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4">
{data.map((row, idx) => {
const keyField = gridDto.gridOptions.keyFieldName
const rowId = row[keyField!]
const handleCardClick = () => {
if (rowId) {
navigate(`/admin/form/${listFormCode}/${rowId}`)
}
}
return (
<div
key={rowId || idx}
className="bg-white dark:bg-neutral-800 hover:shadow-xl hover:border-blue-400 border dark:border-neutral-700 transition-all duration-300 cursor-pointer flex flex-col group"
onClick={handleCardClick}
>
<div className="p-4 flex-grow">
<div className="grid grid-cols-1 gap-y-3">
{gridDto.columnFormats
.filter((col) => col.visible && col.listOrderNo > 0)
.sort((a, b) => a.listOrderNo - b.listOrderNo)
.slice(0, 10) // İlk 10 görünür alanı gösterelim
.map((col, colIdx) => (
<div key={col.fieldName} className="truncate text-sm">
<p className="text-xs text-slate-500 dark:text-slate-400 mb-0.5">
{captionize(col.captionName || col.fieldName)}
</p>
<p
className={classNames(
'truncate',
colIdx === 0
? 'font-semibold text-base text-slate-800 dark:text-slate-100'
: 'text-slate-700 dark:text-slate-300',
)}
title={row[col.fieldName!]}
>
{row[col.fieldName!]}
</p>
</div>
))}
</div>
</div>
<div className="bg-gray-50 dark:bg-neutral-700/50 p-2 border-t border-gray-100 dark:border-neutral-700 flex items-center justify-end text-xs font-semibold text-blue-600 dark:text-blue-400 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
Görüntüle
<FaAngleRight className="ml-1 w-3 h-3" />
</div>
</div>
)
})}
</div>
{gridDto.gridOptions.pagerOptionDto?.visible && totalCount > pageSize && (
<div
className={classNames(
'flex items-center justify-between border-t-2 border-solid gap-4 p-2',
)}
>
<div className="flex items-center gap-2">
<span className="text-xs text-gray-600 dark:text-gray-300">
Toplam {totalCount} kayıt
</span>
<Select
size='xs'
menuPlacement='top'
value={pageSizeOptions.find((o) => o.value === pageSize)}
options={pageSizeOptions}
onChange={(selected) => onPageSizeSelect(selected as Option)}
/>
</div>
<Pagination
currentPage={currentPage}
total={totalCount}
pageSize={pageSize}
onChange={onPageChange}
/>
</div>
)}
</>
)
}
export default CardView

View file

@ -1,5 +1,5 @@
import { useLocation, useParams, useSearchParams } from 'react-router-dom' import { useLocation, useParams, useSearchParams } from 'react-router-dom'
import { useMemo, useState } from 'react' import { useState } from 'react'
import Container from '@/components/shared/Container' import Container from '@/components/shared/Container'
import Grid from './Grid' import Grid from './Grid'
import { FaList, FaTh, FaUser } from 'react-icons/fa' import { FaList, FaTh, FaUser } from 'react-icons/fa'
@ -7,10 +7,9 @@ import { useStoreState } from '@/store/store'
import classNames from 'classnames' import classNames from 'classnames'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { GridDto } from '@/proxy/form/models' import { GridDto } from '@/proxy/form/models'
import CardView from './CardView' import Card from './Card'
import { Button } from '@/components/ui' import { Button } from '@/components/ui'
import navigationIcon from '@/configs/navigation-icon.config' import navigationIcon from '@/configs/navigation-icon.config'
import { NavigationTree } from '@/@types/navigation'
import { navigationTreeToFlat } from '@/utils/navigation' import { navigationTreeToFlat } from '@/utils/navigation'
const List = () => { const List = () => {
@ -86,7 +85,7 @@ const List = () => {
onGridDtoLoad={setGridDto} onGridDtoLoad={setGridDto}
/> />
) : ( ) : (
<CardView listFormCode={listFormCode} searchParams={searchParams} /> <Card listFormCode={listFormCode} searchParams={searchParams} />
)} )}
</Container> </Container>
) )