ReportViewer ve ReportDesigner bundle çıkarıldı

This commit is contained in:
Sedat Öztürk 2026-02-04 20:36:18 +03:00
parent ff3344b3ae
commit b070702378
8 changed files with 1474 additions and 1271 deletions

View file

@ -4667,12 +4667,12 @@ public class ListFormSeeder_SupplyChain : IDataSeedContributor, ITransientDepend
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.PurchaseOrder)), DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.PurchaseOrder)),
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(), DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
PagerOptionJson = DefaultPagerOptionJson, PagerOptionJson = DefaultPagerOptionJson,
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 500, true, true, true, true, false, true), EditingOptionJson = DefaultEditingOptionJson(listFormName, 800, 400, true, true, true, true, false, true),
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(), InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>()
{ {
new() { new() {
Order=1, ColCount=1, ColSpan=1, ItemType="group", Items =[ Order=1, ColCount=2, ColSpan=1, ItemType="group", Items =[
new EditingFormItemDto { Order = 1, DataField="OrderNumber", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxTextBox, EditorOptions = EditorOptionValues.Disabled }, new EditingFormItemDto { Order = 1, DataField="OrderNumber", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxTextBox, EditorOptions = EditorOptionValues.Disabled },
new EditingFormItemDto { Order = 2, DataField="SupplierId", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton }, new EditingFormItemDto { Order = 2, DataField="SupplierId", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
new EditingFormItemDto { Order = 3, DataField="OrderDate", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxDateBox, EditorOptions = EditorOptionValues.DateFormat }, new EditingFormItemDto { Order = 3, DataField="OrderDate", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxDateBox, EditorOptions = EditorOptionValues.DateFormat },

2224
ui/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -37,10 +37,9 @@
"axios": "^1.7.9", "axios": "^1.7.9",
"classnames": "^2.5.1", "classnames": "^2.5.1",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"devexpress-reporting-react": "^25.1.7", "devexpress-reporting-react": "^25.2.3",
"devextreme": "^25.1.7", "devextreme": "^25.2.3",
"devextreme-dist": "^25.1.7", "devextreme-react": "^25.2.3",
"devextreme-react": "^25.1.7",
"easy-peasy": "^6.0.5", "easy-peasy": "^6.0.5",
"emoji-picker-react": "^4.14.1", "emoji-picker-react": "^4.14.1",
"exceljs": "^4.4.0", "exceljs": "^4.4.0",

View file

@ -1,6 +1,19 @@
{ {
"commit": "16dca4a0", "commit": "270e50e0",
"releases": [ "releases": [
{
"version": "1.0.40",
"buildDate": "2026-01-17",
"commit": "52c93ccbaf1be1c8365097e97276f48988864de4",
"changeLog": [
"- Grid üzerinden Dil desteği güncellemeleri yapıldı.",
"- Nodejs 24 versiyonuna geçildi.",
"- Grid ve Popup içerisinde Numeric formatlar geliştirildi.",
"- File Management geliştirildi.",
"- XtraReport viewer ve desing komponentleri geliştirildi.",
"- Devexpress Licenceı artırıldı."
]
},
{ {
"version": "1.0.37", "version": "1.0.37",
"buildDate": "2025-12-07", "buildDate": "2025-12-07",
@ -192,7 +205,7 @@
{ {
"version": "1.0.14", "version": "1.0.14",
"buildDate": "2025-09-22", "buildDate": "2025-09-22",
"commit": "51208b86937484d68b699120d74872067b1c7ef6", "commit": "1c4ab4f8232b4cd2a39fa66f8101664840113ce5",
"changeLog": [ "changeLog": [
"Yeni versiyon çıktı uyarı gelecek şekilde düzenlendi.", "Yeni versiyon çıktı uyarı gelecek şekilde düzenlendi.",
"Sağ alt kısımda mesaj çıkacak ve yenile butonu ile uygulama yeni versiyona geçecektir." "Sağ alt kısımda mesaj çıkacak ve yenile butonu ile uygulama yeni versiyona geçecektir."

View file

@ -1,98 +1,134 @@
import React, { Suspense, startTransition, useCallback, useEffect, useMemo, useState } from 'react'
import { useParams, useSearchParams } from 'react-router-dom' import { useParams, useSearchParams } from 'react-router-dom'
import { useEffect, useState } from 'react'
import Container from '@/components/shared/Container'
import Grid from './Grid'
import { FaChartArea, FaList, FaSitemap, FaTable, FaTh, FaCalendarAlt, FaIdCard, FaProjectDiagram } from 'react-icons/fa'
import { useStoreActions, useStoreState } from '@/store/store'
import classNames from 'classnames' import classNames from 'classnames'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { GridDto } from '@/proxy/form/models' import Container from '@/components/shared/Container'
import { Badge, Button } from '@/components/ui' import { Badge, Button } from '@/components/ui'
import Pivot from './Pivot' import { useLocalization } from '@/utils/hooks/useLocalization'
import Tree from './Tree'
import { getList } from '@/services/form.service'
import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon' import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon'
import { useStoreActions, useStoreState } from '@/store/store'
import { GridDto } from '@/proxy/form/models'
import { getList } from '@/services/form.service'
import { ListViewLayoutType } from '../admin/listForm/edit/types' import { ListViewLayoutType } from '../admin/listForm/edit/types'
import Chart from './Chart'
import GanttView from './GanttView'
import SchedulerView from './SchedulerView'
const List = () => { import {
const params = useParams() FaChartArea,
const { translate } = useLocalization() FaList,
FaSitemap,
FaTable,
FaCalendarAlt,
FaProjectDiagram,
} from 'react-icons/fa'
/* =======================
🔥 LAZY VIEW IMPORTS
======================= */
const Grid = React.lazy(() => import('./Grid'))
const Pivot = React.lazy(() => import('./Pivot'))
const Tree = React.lazy(() => import('./Tree'))
const Chart = React.lazy(() => import('./Chart'))
const GanttView = React.lazy(() => import('./GanttView'))
const SchedulerView = React.lazy(() => import('./SchedulerView'))
const List: React.FC = () => {
const { listFormCode = '' } = useParams()
const [searchParams] = useSearchParams() const [searchParams] = useSearchParams()
const listFormCode = params?.listFormCode ?? ''
const mode = useStoreState((state) => state.theme.mode)
const [viewMode, setViewMode] = useState<ListViewLayoutType>()
const [gridDto, setGridDto] = useState<GridDto>() const { translate } = useLocalization()
const mode = useStoreState((state) => state.theme.mode)
const MenuIcon = useCurrentMenuIcon('w-5 h-5') const MenuIcon = useCurrentMenuIcon('w-5 h-5')
const { states } = useStoreState((state) => state.admin.lists) const { states } = useStoreState((state) => state.admin.lists)
const { setStates } = useStoreActions((a) => a.admin.lists) const { setStates } = useStoreActions((a) => a.admin.lists)
// 🔹 Tekrar çağırılabilir metod const [gridDto, setGridDto] = useState<GridDto | null>(null)
const refreshGridDto = async () => { const [viewMode, setViewMode] = useState<ListViewLayoutType | undefined>()
/* =======================
🔹 GRID DTO FETCH
======================= */
const refreshGridDto = useCallback(async () => {
if (!listFormCode) return if (!listFormCode) return
try { try {
const response = await getList({ listFormCode }) const response = await getList({ listFormCode })
setGridDto(response.data) setGridDto(response.data)
} catch (error) { } catch (err) {
console.error('GridDto refresh error:', error) console.error('GridDto load error:', err)
} }
}
useEffect(() => {
refreshGridDto()
}, [listFormCode]) }, [listFormCode])
useEffect(() => { useEffect(() => {
const listFormStates = states.find((a) => a.listFormCode === listFormCode) refreshGridDto()
}, [refreshGridDto])
if (listFormStates) { /* =======================
setViewMode(listFormStates.layout) 🔹 INITIAL VIEW MODE
return ======================= */
} useEffect(() => {
if (!gridDto) return
if (gridDto) { const savedLayout = states.find((s) => s.listFormCode === listFormCode)?.layout
setViewMode(gridDto?.gridOptions?.layoutDto.defaultLayout)
return
}
}, [gridDto])
if (!listFormCode) { const defaultLayout = gridDto.gridOptions?.layoutDto?.defaultLayout
return null
}
if (!gridDto) { setViewMode(savedLayout ?? defaultLayout)
}, [gridDto, states, listFormCode])
/* =======================
🔹 VIEW SWITCH HANDLER
======================= */
const setLayout = useCallback(
(layout: ListViewLayoutType) => {
startTransition(() => {
setViewMode(layout)
})
setStates({ listFormCode, layout })
},
[listFormCode, setStates],
)
/* =======================
🔹 PRELOAD MAP (HOVER)
======================= */
const preload = useMemo(
() => ({
grid: () => import('./Grid'),
pivot: () => import('./Pivot'),
tree: () => import('./Tree'),
chart: () => import('./Chart'),
gantt: () => import('./GanttView'),
scheduler: () => import('./SchedulerView'),
}),
[],
)
if (!listFormCode || !gridDto || !viewMode) {
return null return null
} }
return ( return (
<Container> <Container>
{/* =======================
HEADER
======================= */}
<div <div
className={classNames('flex items-center border-solid gap-1 pb-1', { className={classNames(
'border-gray-100': mode === 'light', 'flex items-center gap-2 pb-1 border-b',
'border-neutral-700': mode === 'dark', mode === 'light' ? 'border-gray-200' : 'border-neutral-700',
})}
>
<div className="flex items-center gap-2">
{MenuIcon}
<h4 className="text-slate-700 text-sm font-medium leading-none">
{translate('::' + gridDto?.gridOptions?.title) || ''}
</h4>
<Badge content={viewMode} />
</div>
{gridDto?.gridOptions?.description === gridDto?.gridOptions?.title ? (
<p className="mr-auto"></p>
) : (
<p className="text-slate-500 text-xs mr-auto ml-2 leading-none">
{translate('::' + gridDto?.gridOptions?.description)}
</p>
)} )}
>
{MenuIcon}
<h4 className="text-sm font-medium">{translate('::' + gridDto.gridOptions.title)}</h4>
<Badge content={viewMode} />
<p className="ml-2 text-xs text-slate-500 mr-auto">
{translate('::' + gridDto.gridOptions.description)}
</p>
{/* =======================
VIEW BUTTONS
======================= */}
<div className="flex gap-1"> <div className="flex gap-1">
{gridDto?.gridOptions?.layoutDto.scheduler && {gridDto?.gridOptions?.layoutDto.scheduler &&
gridDto?.gridOptions?.schedulerOptionDto?.textExpr && gridDto?.gridOptions?.schedulerOptionDto?.textExpr &&
@ -100,13 +136,10 @@ const List = () => {
<Button <Button
size="xs" size="xs"
variant={viewMode === 'scheduler' ? 'solid' : 'default'} variant={viewMode === 'scheduler' ? 'solid' : 'default'}
onClick={() => { onClick={() => setLayout('scheduler')}
setViewMode('scheduler') onMouseEnter={() => preload.scheduler()}
setStates({ listFormCode, layout: 'scheduler' })
}}
title="Scheduler Görünümü"
> >
<FaCalendarAlt className="w-4 h-4" /> <FaCalendarAlt />
</Button> </Button>
)} )}
@ -116,13 +149,10 @@ const List = () => {
<Button <Button
size="xs" size="xs"
variant={viewMode === 'gantt' ? 'solid' : 'default'} variant={viewMode === 'gantt' ? 'solid' : 'default'}
onClick={() => { onClick={() => setLayout('gantt')}
setViewMode('gantt') onMouseEnter={() => preload.gantt()}
setStates({ listFormCode, layout: 'gantt' })
}}
title="Gantt Görünümü"
> >
<FaProjectDiagram className="w-4 h-4" /> <FaProjectDiagram />
</Button> </Button>
)} )}
@ -131,106 +161,107 @@ const List = () => {
<Button <Button
size="xs" size="xs"
variant={viewMode === 'tree' ? 'solid' : 'default'} variant={viewMode === 'tree' ? 'solid' : 'default'}
onClick={() => { onClick={() => setLayout('tree')}
setViewMode('tree') onMouseEnter={() => preload.tree()}
setStates({ listFormCode, layout: 'tree' })
}}
title="TreeList Görünümü"
> >
<FaSitemap className="w-4 h-4" /> <FaSitemap />
</Button> </Button>
)} )}
{gridDto?.gridOptions?.layoutDto.grid && ( <Button
<Button size="xs"
size="xs" variant={viewMode === 'grid' ? 'solid' : 'default'}
variant={viewMode === 'grid' ? 'solid' : 'default'} onClick={() => setLayout('grid')}
onClick={() => { onMouseEnter={() => preload.grid()}
setViewMode('grid') >
setStates({ listFormCode, layout: 'grid' }) <FaList />
}} </Button>
title="Grid Görünümü"
>
<FaList className="w-4 h-4" />
</Button>
)}
{gridDto?.gridOptions?.layoutDto.pivot && ( {gridDto.gridOptions.layoutDto.pivot && (
<Button <Button
size="xs" size="xs"
variant={viewMode === 'pivot' ? 'solid' : 'default'} variant={viewMode === 'pivot' ? 'solid' : 'default'}
onClick={() => { onClick={() => setLayout('pivot')}
setViewMode('pivot') onMouseEnter={() => preload.pivot()}
setStates({ listFormCode, layout: 'pivot' })
}}
title="Pivot Görünümü"
> >
<FaTable className="w-4 h-4" /> <FaTable />
</Button> </Button>
)} )}
{gridDto?.gridOptions?.layoutDto.chart && ( {gridDto.gridOptions.layoutDto.chart && (
<Button <Button
size="xs" size="xs"
variant={viewMode === 'chart' ? 'solid' : 'default'} variant={viewMode === 'chart' ? 'solid' : 'default'}
onClick={() => { onClick={() => setLayout('chart')}
setViewMode('chart') onMouseEnter={() => preload.chart()}
setStates({ listFormCode, layout: 'chart' })
}}
title="Grafik Görünümü"
> >
<FaChartArea className="w-4 h-4" /> <FaChartArea />
</Button> </Button>
)} )}
</div> </div>
</div> </div>
{viewMode === 'pivot' ? ( {/* =======================
<Pivot VIEW RENDER
listFormCode={listFormCode} ======================= */}
searchParams={searchParams} <Suspense fallback={<div className="p-4 text-sm">Yükleniyor...</div>}>
isSubForm={false} {viewMode === 'grid' && (
gridDto={gridDto} <Grid
refreshGridDto={refreshGridDto} listFormCode={listFormCode}
/> searchParams={searchParams}
) : viewMode === 'scheduler' ? ( isSubForm={false}
<SchedulerView gridDto={gridDto}
listFormCode={listFormCode} />
searchParams={searchParams} )}
isSubForm={false}
gridDto={gridDto} {viewMode === 'pivot' && (
/> <Pivot
) : viewMode === 'gantt' ? ( listFormCode={listFormCode}
<GanttView searchParams={searchParams}
listFormCode={listFormCode} isSubForm={false}
searchParams={searchParams} gridDto={gridDto}
isSubForm={false} refreshGridDto={refreshGridDto}
gridDto={gridDto} />
/> )}
) : viewMode === 'tree' ? (
<Tree {viewMode === 'tree' && (
listFormCode={listFormCode} <Tree
searchParams={searchParams} listFormCode={listFormCode}
isSubForm={false} searchParams={searchParams}
gridDto={gridDto} isSubForm={false}
/> gridDto={gridDto}
) : viewMode === 'grid' ? ( />
<Grid )}
listFormCode={listFormCode}
searchParams={searchParams} {viewMode === 'scheduler' && (
isSubForm={false} <SchedulerView
gridDto={gridDto} listFormCode={listFormCode}
/> searchParams={searchParams}
) : viewMode === 'chart' ? ( isSubForm={false}
<Chart gridDto={gridDto}
id={gridDto?.gridOptions.id!} />
listFormCode={listFormCode} )}
filter={searchParams.toString()}
isSubForm={true} {viewMode === 'gantt' && (
gridDto={gridDto} <GanttView
refreshGridDto={refreshGridDto} listFormCode={listFormCode}
/> searchParams={searchParams}
) : null} isSubForm={false}
gridDto={gridDto}
/>
)}
{viewMode === 'chart' && (
<Chart
id={gridDto?.gridOptions.id!}
listFormCode={listFormCode}
filter={searchParams.toString()}
isSubForm={true}
gridDto={gridDto}
refreshGridDto={refreshGridDto}
/>
)}
</Suspense>
</Container> </Container>
) )
} }

View file

@ -1,37 +1,58 @@
import React from 'react' import React, { lazy, Suspense, useEffect } from 'react'
import { Container } from '@/components/shared' import { Container } from '@/components/shared'
import { Helmet } from 'react-helmet' import { Helmet } from 'react-helmet'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import ReportDesigner, { RequestOptions } from 'devexpress-reporting-react/dx-report-designer'
import '@devexpress/analytics-core/dist/css/dx-analytics.common.css'
import '@devexpress/analytics-core/dist/css/dx-analytics.light.css'
import '@devexpress/analytics-core/dist/css/dx-querybuilder.css'
import 'devexpress-reporting/dist/css/dx-webdocumentviewer.css'
import 'devexpress-reporting/dist/css/dx-reportdesigner.css'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { RequestOptions } from 'devexpress-reporting-react/dx-report-designer'
const ReportDesigner = lazy(() =>
import('devexpress-reporting-react/dx-report-designer')
)
const loadDesignerCss = () => {
const styles = [
new URL('@devexpress/analytics-core/dist/css/dx-analytics.common.css', import.meta.url).href,
new URL('@devexpress/analytics-core/dist/css/dx-analytics.light.css', import.meta.url).href,
new URL('@devexpress/analytics-core/dist/css/dx-querybuilder.css', import.meta.url).href,
new URL('devexpress-reporting/dist/css/dx-webdocumentviewer.css', import.meta.url).href,
new URL('devexpress-reporting/dist/css/dx-reportdesigner.css', import.meta.url).href,
]
styles.forEach((href) => {
if (document.querySelector(`link[href="${href}"]`)) return
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = href
document.head.appendChild(link)
})
}
const DevexpressReportDesigner: React.FC = () => { const DevexpressReportDesigner: React.FC = () => {
const { translate } = useLocalization() const { translate } = useLocalization()
const { id } = useParams<{ id: string }>() const { id } = useParams<{ id: string }>()
if (!id) { useEffect(() => {
return null loadDesignerCss()
} }, [])
if (!id) return null
return ( return (
<Container> <Container>
<Helmet <Helmet
titleTemplate="%s | Erp Platform" titleTemplate="%s | Erp Platform"
title={translate('::' + 'App.Reports')} title={translate('::App.Reports')}
defaultTitle="Erp Platform" defaultTitle="Erp Platform"
></Helmet> />
<ReportDesigner reportUrl={id}> <Suspense fallback={<div>Rapor tasarımcısı yükleniyor...</div>}>
<RequestOptions <ReportDesigner reportUrl={id}>
host={`${import.meta.env.VITE_API_URL}/`} <RequestOptions
getDesignerModelAction="DXXRD/GetDesignerModel" host={`${import.meta.env.VITE_API_URL}/`}
/> getDesignerModelAction="DXXRD/GetDesignerModel"
</ReportDesigner> />
</ReportDesigner>
</Suspense>
</Container> </Container>
) )
} }

View file

@ -1,42 +1,63 @@
import React, { useMemo } from 'react' import React, { useMemo, lazy, Suspense, useEffect } from 'react'
import { Container } from '@/components/shared' import { Container } from '@/components/shared'
import { Helmet } from 'react-helmet' import { Helmet } from 'react-helmet'
import ReportViewer, { RequestOptions } from 'devexpress-reporting-react/dx-report-viewer'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import 'devextreme/dist/css/dx.light.css'
import '@devexpress/analytics-core/dist/css/dx-analytics.common.css'
import '@devexpress/analytics-core/dist/css/dx-analytics.light.css'
import 'devexpress-reporting/dist/css/dx-webdocumentviewer.css'
import { useParams, useLocation } from 'react-router-dom' import { useParams, useLocation } from 'react-router-dom'
import { RequestOptions } from 'devexpress-reporting-react/dx-report-viewer'
// Lazy load
const ReportViewer = lazy(() =>
import('devexpress-reporting-react/dx-report-viewer')
)
const loadViewerCss = () => {
const styles = [
new URL('@devexpress/analytics-core/dist/css/dx-analytics.common.css', import.meta.url).href,
new URL('@devexpress/analytics-core/dist/css/dx-analytics.light.css', import.meta.url).href,
new URL('devexpress-reporting/dist/css/dx-webdocumentviewer.css', import.meta.url).href,
]
styles.forEach((href) => {
if (document.querySelector(`link[href="${href}"]`)) return
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = href
document.head.appendChild(link)
})
}
const DevexpressReportViewer: React.FC = () => { const DevexpressReportViewer: React.FC = () => {
const { translate } = useLocalization() const { translate } = useLocalization()
const { id } = useParams<{ id: string }>() const { id } = useParams<{ id: string }>()
const location = useLocation() const location = useLocation()
// Query string parametrelerini reportUrl'e ekle useEffect(() => {
loadViewerCss()
}, [])
const reportUrlWithParams = useMemo(() => { const reportUrlWithParams = useMemo(() => {
if (location.search) { if (!id) return ''
return `${id}${location.search}` return location.search ? `${id}${location.search}` : id
}
return id
}, [id, location.search]) }, [id, location.search])
if (!id) { if (!id) return null
return null
}
return ( return (
<Container> <Container>
<Helmet <Helmet
titleTemplate="%s | Erp Platform" titleTemplate="%s | Erp Platform"
title={translate('::' + 'App.Reports')} title={translate('::App.Reports')}
defaultTitle="Erp Platform" defaultTitle="Erp Platform"
></Helmet> />
<ReportViewer reportUrl={reportUrlWithParams}> <Suspense fallback={<div>Rapor yükleniyor...</div>}>
<RequestOptions host={`${import.meta.env.VITE_API_URL}/`} invokeAction="DXXRDV" /> <ReportViewer reportUrl={reportUrlWithParams}>
</ReportViewer> <RequestOptions
host={`${import.meta.env.VITE_API_URL}/`}
invokeAction="DXXRDV"
/>
</ReportViewer>
</Suspense>
</Container> </Container>
) )
} }

View file

@ -37,7 +37,7 @@ export default defineConfig(async ({ mode }) => {
globPatterns: ['**/*.{js,css,html,wasm}'], globPatterns: ['**/*.{js,css,html,wasm}'],
// Büyük asset'leri de cache'leyebil // Büyük asset'leri de cache'leyebil
maximumFileSizeToCacheInBytes: 10 * 1024 * 1024, maximumFileSizeToCacheInBytes: 15 * 1024 * 1024,
// EN KRİTİK: yeni SW beklemeden kontrolü alsın // EN KRİTİK: yeni SW beklemeden kontrolü alsın
clientsClaim: true, clientsClaim: true,
@ -139,6 +139,7 @@ export default defineConfig(async ({ mode }) => {
build: { build: {
outDir: 'dist', outDir: 'dist',
chunkSizeWarningLimit: 2000,
sourcemap: false, sourcemap: false,
emptyOutDir: true, emptyOutDir: true,
rollupOptions: { rollupOptions: {
@ -154,6 +155,41 @@ export default defineConfig(async ({ mode }) => {
} }
return 'assets/[name]-[hash][extname]' return 'assets/[name]-[hash][extname]'
}, },
manualChunks(id) {
// 🔴 DEVEXTREME (esm + non-esm + react wrapper)
if (
id.includes('node_modules/devextreme') ||
id.includes('node_modules/devextreme-react')
) {
return 'dx-framework'
}
// 🔴 REPORTING
if (id.includes('devexpress-reporting') || id.includes('@devexpress')) {
return 'dx-reporting'
}
// 🟣 EXPORT TOOLS
if (
id.includes('exceljs') ||
id.includes('xlsx') ||
id.includes('jspdf') ||
id.includes('html2canvas') ||
id.includes('file-saver')
) {
return 'export-tools'
}
// 🟡 EDITOR
if (id.includes('monaco-editor') || id.includes('codemirror')) {
return 'editor'
}
// ⚪ KALAN VENDOR
if (id.includes('node_modules')) {
return 'vendor'
}
},
}, },
}, },
}, },