18 Commits

Author SHA1 Message Date
a10f6997eb Add 'Çekiliş Hakkı Adedi' field to piyangoKatilimciStore and implement validation in piyangoKatilimciValidationStore. Ensure the value is at least 1 to maintain data integrity during form submissions. 2026-03-26 12:38:56 +03:00
2bbb30525e Enhance error handling in useDataStore by adding skipErrorForStatuses option to bypass specific status codes in CheckApiError. This improves error management and allows for more flexible API response handling. 2026-03-26 12:38:43 +03:00
2ecda61ad4 Add removeUploadHandlers function to signalrService for improved memory management by detaching event handlers, preventing memory leaks and multiple triggers. 2026-03-26 12:38:29 +03:00
3c4db6d312 Update SaveKatilimciUser function to ensure 'Çekiliş Hakkı Adedi' is always at least 1, enhancing data integrity during user form submissions. 2026-03-26 12:38:18 +03:00
e9e83827dc Update PanelPiyangoKatilimci component to replace 'Katılım Sıra No' with 'Çekiliş Hakkı Adedi' for improved clarity. Add new input field for 'Çekiliş Hakkı Adedi' with validation support, enhancing user interaction and data entry accuracy. 2026-03-26 12:38:07 +03:00
1b8c10a8a0 Enhance PanelExcelUploadDetail component by adding detailed job statistics, including inserted, skipped, duplicate, and error counts. Update grid layout to accommodate new statistics for improved data presentation. 2026-03-26 12:37:56 +03:00
83acb64944 Add 'Çekiliş Hakkı Adedi' column to TabPiyangoTalihliListesi component for improved data display. 2026-03-26 12:37:45 +03:00
cbbd5b2abe Enhance TabPiyangoKatilimciListesi component by adding conditional rendering for job completion statistics, including inserted and duplicate counts. Implement toast notifications for handling upload conflicts and ensure proper cleanup of upload handlers on component unmount. This improves user feedback and data management during file uploads. 2026-03-26 12:37:32 +03:00
a75158379d katılımcı listesi 2026-03-18 07:47:18 +03:00
46d0cac892 Close dialog for "basvurubedelimuhasebelestir" after loading data in FormMuhasebePiyangoBilgileriDisplay component. This improves user experience by ensuring the dialog is properly closed upon data retrieval. 2025-12-23 04:35:22 +03:00
f8a3940643 Enhance PiyangoListe and MuhasebePiyangoListesi components by adding "Oluşturma Tarihi", "Sevk Durumu", and "Muhasebeleştirme Durumu" columns. Update date formatting and conditional rendering for improved data presentation. Remove redundant code for special columns to streamline component structure. 2025-12-18 16:20:08 +03:00
078a5a020a Refactor PiyangoOnay components to consolidate conditional rendering for "Müdürlük" and "Çekiliş Görevlisi" fields. Update validation and service logic to ensure proper handling based on "onayDurumuIslemTipiId". This enhances the user experience by streamlining form interactions and data management. 2025-12-18 15:01:55 +03:00
edb1d23d71 Implement custom scrollbar for DataTable component. Refactor structure to include a scrollable wrapper and synchronize scroll positions between the table and the custom scrollbar. Update styles for improved visibility and functionality of the scrollbar. 2025-12-18 12:36:51 +03:00
bf9c2b25f9 Add conditional rendering for "Müdürlük" and "Çekiliş Görevlisi" fields in PiyangoOnay components. Update validation logic to require these fields based on "onayDurumuIslemTipiId". Enhance service layer to handle new fields and ensure proper data submission. 2025-12-17 16:04:20 +03:00
4f29c18dab Enhance date formatting in PiyangoListe and MuhasebePiyangoListesi components. Update date handling to include specific patterns and split options for improved readability. Add new columns for "Oluşturma Tarihi" and "Sevk Durumu" in MuhasebePiyangoListesi, while removing redundant code for special columns. 2025-12-17 15:38:07 +03:00
8cfda2449e Refactor row action handling in FormPiyangoOnayDurum component. Replace EditOnay with adminRowAction and update rowActions to adminRowActions based on user role, enhancing access control and functionality. 2025-12-12 14:36:26 +03:00
ddd8594372 Refactor PiyangoOnay components to replace refreshList logic with window reload. This change simplifies the refresh mechanism after form submission, enhancing user experience by ensuring the latest data is displayed immediately. 2025-12-11 14:08:02 +03:00
4a081fe249 Update PiyangoDetay component to change conditional rendering for the "Dosya Kapama" tab. The condition now checks for piyangoStore.lotteryApprove === 10 instead of usersStore.isPanelUser && piyangoStore.lotteryDrawState to enhance logic clarity and functionality. 2025-12-11 13:55:19 +03:00
24 changed files with 1324 additions and 353 deletions

128
package-lock.json generated
View File

@ -15,8 +15,6 @@
"jquery": "^3.7.1", "jquery": "^3.7.1",
"parchment": "^3.0.0", "parchment": "^3.0.0",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"quill": "^2.0.3",
"quill-image-resize-module": "^3.0.0",
"summernote": "^0.9.1", "summernote": "^0.9.1",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"vue": "^3.4.29", "vue": "^3.4.29",
@ -1718,14 +1716,6 @@
} }
] ]
}, },
"node_modules/clone": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
"engines": {
"node": ">=0.8"
}
},
"node_modules/combined-stream": { "node_modules/combined-stream": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@ -2006,11 +1996,6 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
},
"node_modules/eventsource": { "node_modules/eventsource": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz",
@ -2019,11 +2004,6 @@
"node": ">=12.0.0" "node": ">=12.0.0"
} }
}, },
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"node_modules/fetch-cookie": { "node_modules/fetch-cookie": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-2.2.0.tgz", "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-2.2.0.tgz",
@ -2359,26 +2339,6 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"node_modules/lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
},
"node_modules/lru-cache": { "node_modules/lru-cache": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@ -2752,94 +2712,6 @@
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
}, },
"node_modules/quill": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz",
"integrity": "sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==",
"dependencies": {
"eventemitter3": "^5.0.1",
"lodash-es": "^4.17.21",
"parchment": "^3.0.0",
"quill-delta": "^5.1.0"
},
"engines": {
"npm": ">=8.2.3"
}
},
"node_modules/quill-image-resize-module": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/quill-image-resize-module/-/quill-image-resize-module-3.0.0.tgz",
"integrity": "sha512-1TZBnUxU/WIx5dPyVjQ9yN7C6mLZSp04HyWBEMqT320DIq4MW4JgzlOPDZX5ZpBM3bU6sacU4kTLUc8VgYQZYw==",
"dependencies": {
"lodash": "^4.17.4",
"quill": "^1.2.2",
"raw-loader": "^0.5.1"
}
},
"node_modules/quill-image-resize-module/node_modules/eventemitter3": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
"integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg=="
},
"node_modules/quill-image-resize-module/node_modules/fast-diff": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
"integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig=="
},
"node_modules/quill-image-resize-module/node_modules/parchment": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg=="
},
"node_modules/quill-image-resize-module/node_modules/quill": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz",
"integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==",
"dependencies": {
"clone": "^2.1.1",
"deep-equal": "^1.0.1",
"eventemitter3": "^2.0.3",
"extend": "^3.0.2",
"parchment": "^1.1.4",
"quill-delta": "^3.6.2"
}
},
"node_modules/quill-image-resize-module/node_modules/quill-delta": {
"version": "3.6.3",
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
"integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
"dependencies": {
"deep-equal": "^1.0.1",
"extend": "^3.0.2",
"fast-diff": "1.1.2"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/quill/node_modules/fast-diff": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="
},
"node_modules/quill/node_modules/quill-delta": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz",
"integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==",
"dependencies": {
"fast-diff": "^1.3.0",
"lodash.clonedeep": "^4.5.0",
"lodash.isequal": "^4.5.0"
},
"engines": {
"node": ">= 12.0.0"
}
},
"node_modules/raw-loader": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
"integrity": "sha512-sf7oGoLuaYAScB4VGr0tzetsYlS8EJH6qnTCfQ/WVEa89hALQ4RQfCKt5xCyPQKPDUbVUAIP1QsxAwfAjlDp7Q=="
},
"node_modules/read-package-json-fast": { "node_modules/read-package-json-fast": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz",

View File

@ -164,3 +164,4 @@ FROM Cekilisler c LEFT JOIN
paraBirimi.ParaBirimiSembol, ts .TeminantDate, ts .TeminantNo, ts .BankName, ts .Amount, ts .RefundDate, ts .RefundCount, odlast.Mudurluk, odlast.CekilisGorevlisi; paraBirimi.ParaBirimiSembol, ts .TeminantDate, ts .TeminantNo, ts .BankName, ts .Amount, ts .RefundDate, ts .RefundCount, odlast.Mudurluk, odlast.CekilisGorevlisi;

View File

@ -1246,6 +1246,44 @@ section {
padding: 10px; padding: 10px;
} }
.list-wrapper-container {
width: 100%;
position: relative;
}
.list-wrapper-scrollbar-top {
width: 100%;
overflow-x: auto;
overflow-y: hidden;
height: 17px;
margin-bottom: 0;
display: none;
position: relative;
z-index: 10;
}
.list-wrapper-scrollbar-top .scrollbar-content {
height: 1px;
width: 0;
}
.list-wrapper-scrollbar-top::-webkit-scrollbar {
height: 17px;
}
.list-wrapper-scrollbar-top::-webkit-scrollbar-track {
background: transparent;
}
.list-wrapper-scrollbar-top::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 10px;
}
.list-wrapper-scrollbar-top::-webkit-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 0.3);
}
.list-wrapper { .list-wrapper {
width: 100%; width: 100%;
position: relative; position: relative;
@ -2374,6 +2412,14 @@ section {
width: 100% !important; width: 100% !important;
} }
.list-wrapper-container {
overflow: visible;
}
.list-wrapper-scrollbar-top {
display: none !important;
}
.list-wrapper { .list-wrapper {
overflow: visible; overflow: visible;
overflow-x: visible; overflow-x: visible;

View File

@ -1,6 +1,10 @@
<template> <template>
<div class="list-wrapper"> <div class="list-wrapper-container">
<table class="table-border table-colored table-list"> <div class="list-wrapper-scrollbar-top" ref="scrollbarTopRef">
<div class="scrollbar-content"></div>
</div>
<div class="list-wrapper" ref="listWrapperRef" @scroll="onScroll">
<table class="table-border table-colored table-list">
<thead> <thead>
<tr> <tr>
<th class="table-head-row-number" v-if="rowNumber"> <th class="table-head-row-number" v-if="rowNumber">
@ -135,6 +139,7 @@
</template> </template>
</tbody> </tbody>
</table> </table>
</div>
</div> </div>
<data-table-pagination <data-table-pagination
v-if="pagination !== undefined && showPagination && !isPreview" v-if="pagination !== undefined && showPagination && !isPreview"
@ -142,7 +147,7 @@
:isUseRoute="isUseRoute" /> :isUseRoute="isUseRoute" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, computed, watch } from 'vue' import { ref, reactive, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { useGlobalStore } from '@/stores/globalStore' import { useGlobalStore } from '@/stores/globalStore'
import icourl from '@/assets/images/icons.svg' import icourl from '@/assets/images/icons.svg'
@ -172,7 +177,7 @@
} }
export interface Props { export interface Props {
tableHeader: ITableHead[] tableHeader: ITableHead[]
tableData: Record<string, any> tableData: Record<string, any>[]
rowAction?: Function | string rowAction?: Function | string
pagination?: IPagination pagination?: IPagination
sortData?: ISort sortData?: ISort
@ -285,6 +290,95 @@
}, },
{ deep: true } { deep: true }
) )
watch(
() => props.tableData,
() => {
// Tablo verisi değiştiğinde scrollbar'ı güncelle
setTimeout(() => {
syncScrollbars()
}, 100)
},
{ deep: true }
)
const listWrapperRef = ref<HTMLElement | null>(null)
const scrollbarTopRef = ref<HTMLElement | null>(null)
const onScroll = (e: Event) => {
const target = e.target as HTMLElement
if (scrollbarTopRef.value) {
scrollbarTopRef.value.scrollLeft = target.scrollLeft
}
}
const syncScrollbars = () => {
if (listWrapperRef.value && scrollbarTopRef.value) {
const table = listWrapperRef.value.querySelector('table')
if (table) {
const tableWidth = table.scrollWidth
const wrapperWidth = listWrapperRef.value.clientWidth
if (tableWidth > wrapperWidth) {
scrollbarTopRef.value.style.display = 'block'
scrollbarTopRef.value.scrollLeft = listWrapperRef.value.scrollLeft
// Scrollbar wrapper'ın içeriğinin genişliğini tablo genişliğine eşitle
const scrollbarContent = scrollbarTopRef.value.querySelector('.scrollbar-content') as HTMLElement | null
if (scrollbarContent) {
scrollbarContent.style.width = tableWidth + 'px'
scrollbarContent.style.height = '1px'
}
} else {
scrollbarTopRef.value.style.display = 'none'
}
}
}
}
const onTopScroll = (e: Event) => {
const target = e.target as HTMLElement
if (listWrapperRef.value) {
listWrapperRef.value.scrollLeft = target.scrollLeft
}
}
onMounted(async () => {
await nextTick()
if (scrollbarTopRef.value) {
scrollbarTopRef.value.addEventListener('scroll', onTopScroll)
}
// İlk scrollbar senkronizasyonu
setTimeout(() => {
syncScrollbars()
}, 100)
const resizeObserver = new ResizeObserver(() => {
syncScrollbars()
})
if (listWrapperRef.value) {
resizeObserver.observe(listWrapperRef.value)
}
const tableObserver = new MutationObserver(() => {
syncScrollbars()
})
if (listWrapperRef.value) {
const table = listWrapperRef.value.querySelector('table')
if (table) {
tableObserver.observe(table, { childList: true, subtree: true, attributes: true })
}
}
})
onUnmounted(() => {
if (scrollbarTopRef.value) {
scrollbarTopRef.value.removeEventListener('scroll', onTopScroll)
}
})
</script> </script>
<style scoped> <style scoped>
.action-fixed { .action-fixed {

View File

@ -392,9 +392,22 @@
const searchChanging = ref<boolean>(false) const searchChanging = ref<boolean>(false)
const filterChanging = ref<boolean>(false) const filterChanging = ref<boolean>(false)
const isQueryEqual = (a: Record<string, any>, b: Record<string, any>) => {
const keys = new Set([...Object.keys(a || {}), ...Object.keys(b || {})])
for (const k of keys) {
const va = a?.[k]
const vb = b?.[k]
if (Array.isArray(va) && Array.isArray(vb)) {
if (va.length !== vb.length || va.some((v: any, i: number) => v !== vb[i]))
return false
} else if (String(va ?? '') !== String(vb ?? '')) return false
}
return true
}
const RoutePageControl = () => { const RoutePageControl = () => {
if (props.isUseRoute) { if (props.isUseRoute) {
const q = { ...route.query } const q = { ...route.query } as Record<string, any>
pageNumberChanging.value = true pageNumberChanging.value = true
if (q.pageNumber !== undefined && q.pageNumber !== '') { if (q.pageNumber !== undefined && q.pageNumber !== '') {
@ -404,7 +417,9 @@
localPagination.value.pageNumber = localPagination.value.pageNumber || 1 localPagination.value.pageNumber = localPagination.value.pageNumber || 1
} }
router.push({ query: q }) if (!isQueryEqual(q, route.query as Record<string, any>)) {
router.push({ query: q })
}
nextTick(() => { nextTick(() => {
pageNumberChanging.value = false pageNumberChanging.value = false
}) })
@ -413,7 +428,7 @@
const RouteSortControl = () => { const RouteSortControl = () => {
if (props.isUseRoute) { if (props.isUseRoute) {
const q = { ...route.query } const q = { ...route.query } as Record<string, any>
sortChanging.value = true sortChanging.value = true
if (q.sortOrder !== undefined && q.sortOrder !== null && q.sortOrder !== '') { if (q.sortOrder !== undefined && q.sortOrder !== null && q.sortOrder !== '') {
localSort.value.sortColumn = q.sortColumn as string localSort.value.sortColumn = q.sortColumn as string
@ -424,7 +439,9 @@
delete localSort.value.sortColumn delete localSort.value.sortColumn
delete localSort.value.sortOrder delete localSort.value.sortOrder
} }
router.push({ query: q }) if (!isQueryEqual(q, route.query as Record<string, any>)) {
router.push({ query: q })
}
nextTick(() => { nextTick(() => {
sortChanging.value = false sortChanging.value = false
}) })
@ -433,7 +450,7 @@
const RouteSearchControl = () => { const RouteSearchControl = () => {
if (props.isUseRoute) { if (props.isUseRoute) {
const q = { ...route.query } const q = { ...route.query } as Record<string, any>
searchChanging.value = true searchChanging.value = true
if ( if (
q.searchString !== undefined && q.searchString !== undefined &&
@ -445,7 +462,9 @@
localQuery.value = '' localQuery.value = ''
delete q.searchString delete q.searchString
} }
router.push({ query: q }) if (!isQueryEqual(q, route.query as Record<string, any>)) {
router.push({ query: q })
}
nextTick(() => { nextTick(() => {
searchChanging.value = false searchChanging.value = false
}) })

View File

@ -59,9 +59,8 @@ axios.interceptors.response.use(
const usersStore = useUsersStore() const usersStore = useUsersStore()
const dataStore = useDataStore() const dataStore = useDataStore()
dataStore.isLoading = false dataStore.isLoading = false
// Yanıtta hata oluşursa burada yakalanır // Yanıtta hata oluşursa burada yakalanır (401 login'e yönlendir, diğerleri dataStore catch'te toast gösterir)
// error.status kodu undefined geliyor if (error.response?.status === 401) {
if (error.response.status === 401) {
const token = sessionStorage.getItem(usersStore.userStorageKeys.TOKEN) const token = sessionStorage.getItem(usersStore.userStorageKeys.TOKEN)
if (token !== undefined) { if (token !== undefined) {
usersStore.ResetUserData() usersStore.ResetUserData()

View File

@ -51,11 +51,54 @@
</template> </template>
</list-table-content> </list-table-content>
<panel-wrapper <panel-wrapper
wide
v-if="piyangoKatilimciStore.katilimciFilePanel" v-if="piyangoKatilimciStore.katilimciFilePanel"
v-model="piyangoKatilimciStore.katilimciFilePanel" v-model="piyangoKatilimciStore.katilimciFilePanel"
panel-title="Katılımcı Dosyası Yükle"> panel-title="Katılımcı Dosyası Yükle">
<template #panelContent> <template #panelContent>
<panel-katilimci-document /> <div class="upload-panel-content">
<div v-if="uploadProgressPanel" class="upload-progress-section">
<h4 class="upload-progress-title">Aktif Yükleme</h4>
<div class="upload-warning">
İşlem sırasında tarayıcıyı veya bu sekmeyi kapatmayınız. Sayfa yenilerseniz bu paneli tekrar açarak takip edebilirsiniz.
</div>
<div class="progress-bar">
<div
class="progress-fill"
:style="{ width: uploadProgressValue + '%' }"></div>
</div>
<div class="progress-text">
<template v-if="uploadProgressValue === 0">
Dosya içeriği okunuyor, yükleme başlatılıyor...
</template>
<template v-else>{{ uploadProgressValue }}%</template>
</div>
</div>
<div v-if="uploadJobsLoading || uploadJobs.length > 0" class="upload-history-section">
<h4 class="upload-history-title">Yükleme Geçmişi</h4>
<div v-if="uploadJobsLoading" class="history-loading">Yükleniyor...</div>
<div v-else-if="uploadJobs.length > 0" class="history-items">
<div
v-for="job in uploadJobs"
:key="job.guid"
:class="['history-item', job.status?.toLowerCase() === 'processing' ? 'processing' : '']"
@click="OpenUploadDetail(job.guid)">
<span class="history-file">{{ job.fileName }}</span>
<span :class="['history-status', 'status-' + job.status?.toLowerCase()]">{{ statusText(job.status) }}</span>
<span class="history-progress">{{ job.progress }}%</span>
<span v-if="job.status?.toLowerCase() === 'completed'" class="history-stats">
+{{ job.insertedCount ?? 0 }}
<template v-if="(job.duplicateCount ?? 0) > 0"> / {{ job.duplicateCount }} dpl</template>
</span>
<span class="history-date">{{ formatDate(job.createdAt) }}</span>
</div>
</div>
</div>
<div class="upload-form-section">
<h4 class="upload-form-title">Yeni Dosya Yükle</h4>
<panel-katilimci-document />
</div>
</div>
</template> </template>
<template #footerButton> <template #footerButton>
<button <button
@ -88,26 +131,12 @@
</template> </template>
</panel-wrapper> </panel-wrapper>
<panel-wrapper <panel-wrapper
v-if="uploadProgressPanel" wide
v-model="uploadProgressPanel" v-if="uploadDetailPanel"
panel-title="Yükleme Durumu"> v-model="uploadDetailPanel"
panel-title="Yükleme Detayı">
<template #panelContent> <template #panelContent>
<div class="progress-container"> <panel-excel-upload-detail :jobGuid="selectedJobGuid" />
<div class="upload-warning">
📌 İşlem sırasında tarayıcıyı veya bu sekmeyi kapatmayınız.
</div>
<div class="progress-bar">
<div
class="progress-fill"
:style="{ width: uploadProgressValue + '%' }"></div>
</div>
<div class="progress-text">
<template v-if="uploadProgressValue === 0">
Dosya içeriği okunuyor, yükleme başlatılıyor...
</template>
<template v-else>{{ uploadProgressValue }}%</template>
</div>
</div>
</template> </template>
</panel-wrapper> </panel-wrapper>
</section> </section>
@ -117,6 +146,8 @@
import PanelWrapper from '@/components/PanelWrapper.vue' import PanelWrapper from '@/components/PanelWrapper.vue'
import PanelKatilimciDocument from '@/module/cekilisler/components/panel/PanelKatilimciDocument.vue' import PanelKatilimciDocument from '@/module/cekilisler/components/panel/PanelKatilimciDocument.vue'
import PanelPiyangoKatilimci from '@/module/cekilisler/components/panel/PanelPiyangoKatilimci.vue' import PanelPiyangoKatilimci from '@/module/cekilisler/components/panel/PanelPiyangoKatilimci.vue'
import PanelExcelUploadDetail from '@/module/cekilisler/components/panel/PanelExcelUploadDetail.vue'
import { useDateStore } from '@/stores/dateStore'
import { useUsersStore } from '@/stores/usersStore' import { useUsersStore } from '@/stores/usersStore'
const usersStore = useUsersStore() const usersStore = useUsersStore()
@ -137,18 +168,26 @@
import { useDialogStore } from '@/components/global/dialogStore' import { useDialogStore } from '@/components/global/dialogStore'
const dialogStore = useDialogStore() const dialogStore = useDialogStore()
const toastStore = useToastStore()
import { usePiyangoOnayStore } from '../stores/piyangoOnayStore' import { usePiyangoOnayStore } from '../stores/piyangoOnayStore'
const piyangoOnayStore = usePiyangoOnayStore() const piyangoOnayStore = usePiyangoOnayStore()
import { usePiyangoOnayService } from '../service/piyangoOnayService' import { usePiyangoOnayService } from '../service/piyangoOnayService'
const piyangoOnayService = usePiyangoOnayService() const piyangoOnayService = usePiyangoOnayService()
import { connectToHub, onProgress, onCompleted } from '../service/signalrService' import { onUnmounted } from 'vue'
import { connectToHub, onProgress, onInsertProgress, onCompleted, onError, removeUploadHandlers } from '../service/signalrService'
import { useToastStore } from '@/components/global/toastStore'
const uploadProgressValue = ref(0) const uploadProgressValue = ref(0)
const uploadProgressPanel = ref(false) const uploadProgressPanel = ref(false)
const uploadDetailPanel = ref(false)
const uploadJobs = ref<Record<string, any>[]>([])
const uploadJobsLoading = ref(false)
const selectedJobGuid = ref('')
const connectionId = ref('') const connectionId = ref('')
const dateStore = useDateStore()
let pollInterval: ReturnType<typeof setInterval> | null = null
const tableHeader = ref<Record<string, any>[]>([ const tableHeader = ref<Record<string, any>[]>([
{ {
@ -255,12 +294,57 @@
}) })
const AddNewDocument = async () => { const AddNewDocument = async () => {
connectionId.value = await connectToHub() try {
dataStore.panelData = { connectionId.value = await connectToHub()
title: '', } catch {
file: '' connectionId.value = ''
} }
dataStore.panelData = { title: '', file: '' }
piyangoKatilimciStore.katilimciFilePanel = true piyangoKatilimciStore.katilimciFilePanel = true
uploadJobsLoading.value = true
const cekilisId = piyangoStore.selectedLottery
uploadJobs.value = cekilisId !== null ? await piyangoKatilimciService.GetUploadJobs(cekilisId) : []
uploadJobsLoading.value = false
removeUploadHandlers()
// İşleniyor durumundaki job varsa SignalR ile abone ol (gerçek zamanlı progress almak için)
const processingJob = uploadJobs.value.find(
(j: Record<string, any>) => j.status?.toLowerCase() === 'processing'
)
if (processingJob?.guid && connectionId.value) {
const updateProgress = (data: any) => {
const percent = data.Percent ?? 0
uploadProgressValue.value = percent
const idx = uploadJobs.value.findIndex((j: Record<string, any>) => j.guid === processingJob.guid)
if (idx >= 0) {
uploadJobs.value[idx] = {
...uploadJobs.value[idx],
progress: percent,
processedRows: data.Current ?? data.InsertedCount,
totalRows: data.Total ?? data.TotalCount
}
}
}
onProgress(updateProgress)
onInsertProgress(updateProgress)
onCompleted(() => {
stopPolling()
uploadProgressPanel.value = false
piyangoKatilimciStore.refreshPiyangoKatilimciList = true
piyangoKatilimciStore.katilimciFilePanel = true
refreshUploadJobsAndPollProcessing()
})
onError(() => {
stopPolling()
uploadProgressPanel.value = false
refreshUploadJobsAndPollProcessing()
})
uploadProgressPanel.value = true
uploadProgressValue.value = processingJob.progress ?? 0
await piyangoKatilimciService.SubscribeToJob(processingJob.guid, connectionId.value)
}
startPollingForAnyProcessingJob()
} }
const AddNewKatilimci = () => { const AddNewKatilimci = () => {
@ -280,42 +364,59 @@
piyangoKatilimciStore.katilimciUserPanel = true piyangoKatilimciStore.katilimciUserPanel = true
} }
const FileUpload = async () => { const FileUpload = async () => {
// Mevcut bağlantıyı kullan (AddNewDocument'te açıldı) if (!piyangoKatilimciValidationStore.FileFormCheck()) {
piyangoKatilimciValidationStore.isFileFormValid = true
return
}
// Progress modal'ı removeUploadHandlers()
uploadProgressValue.value = 0 uploadProgressValue.value = 0
uploadProgressPanel.value = true uploadProgressPanel.value = true
onProgress((data) => { onProgress((data) => {
uploadProgressValue.value = data.Percent uploadProgressValue.value = data.Percent
console.log('Progress:', data.Percent)
}) })
onCompleted((data) => { onCompleted(() => {
console.log('Tamamlandı:', data) stopPolling()
uploadProgressPanel.value = false uploadProgressPanel.value = false
piyangoKatilimciStore.refreshPiyangoKatilimciList = true piyangoKatilimciStore.refreshPiyangoKatilimciList = true
piyangoKatilimciStore.katilimciFilePanel = false piyangoKatilimciStore.katilimciFilePanel = true
refreshUploadJobsAndPollProcessing()
})
onError(() => {
stopPolling()
uploadProgressPanel.value = false
}) })
const formData = new FormData() const formData = new FormData()
formData.append( formData.append('excelFile', piyangoKatilimciStore.piyangoKatilimciFileFormData.excelFile)
'excelFile',
piyangoKatilimciStore.piyangoKatilimciFileFormData.excelFile const connId = connectionId.value || ''
)
console.log(dataStore.panelData)
const response = await dataStore.dataPost( const response = await dataStore.dataPost(
`Katilimci/ExcelleYukle/${piyangoStore.selectedLottery}?connectionId=${connectionId.value}`, `Katilimci/ExcelleYukle/${piyangoStore.selectedLottery}?connectionId=${connId}`,
{ {
data: formData, data: formData,
headers: { 'Content-Type': 'multipart/form-data' } headers: { 'Content-Type': 'multipart/form-data' },
skipErrorForStatuses: [409]
} }
) )
console.log('excel response', response)
if (response !== 'errorfalse') { if (response?._error && response.status === 409) {
// Başarı işlemi zaten onCompleted içinde yapıldı uploadProgressPanel.value = false
const msg = response.data?.message || 'Bu çekiliş için zaten devam eden bir Excel yükleme işlemi var.'
toastStore.AddToast(msg, 'alert', 6000)
if (response.data?.existingJobId) {
refreshUploadJobsAndPollProcessing()
OpenUploadDetail(response.data.existingJobId)
}
return
}
if (response !== 'errorfalse' && response?.jobId) {
startPolling(response.jobId)
} else { } else {
// Hata olursa paneli kapat
uploadProgressPanel.value = false uploadProgressPanel.value = false
} }
} }
@ -352,16 +453,92 @@
piyangoOnayStore.piyangoOnayForm.aciklama = '' piyangoOnayStore.piyangoOnayForm.aciklama = ''
await piyangoOnayService.SaveOnayDurum() await piyangoOnayService.SaveOnayDurum()
} }
</script>
<style> const statusText = (status: string) => {
.progress-container { const map: Record<string, string> = {
width: 100%; Pending: 'Bekliyor',
padding: 20px 0; Processing: 'İşleniyor',
display: flex; Completed: 'Tamamlandı',
flex-direction: column; Failed: 'Başarısız'
align-items: center; }
return map[status] || status
} }
const formatDate = (val: string | Date) => {
if (!val) return '-'
return dateStore.dateFormat({ date: new Date(val), pattern: 'dd.mm.yyyy HH:MM' })
}
const OpenUploadDetail = (guid: string) => {
selectedJobGuid.value = guid
uploadDetailPanel.value = true
}
const stopPolling = () => {
if (pollInterval) {
clearInterval(pollInterval)
pollInterval = null
}
}
const startPolling = (jobId: string) => {
stopPolling()
pollInterval = setInterval(async () => {
const status = await piyangoKatilimciService.GetUploadJobStatus(jobId)
if (status && (status.status === 'Completed' || status.status === 'Failed')) {
stopPolling()
uploadProgressValue.value = status.status === 'Completed' ? 100 : status.progress || 0
uploadProgressPanel.value = false
piyangoKatilimciStore.refreshPiyangoKatilimciList = true
piyangoKatilimciStore.katilimciFilePanel = true
refreshUploadJobsAndPollProcessing()
} else if (status) {
uploadProgressValue.value = status.progress || 0
}
}, 2000)
}
const startPollingForExistingJob = (jobGuid: string) => {
stopPolling()
pollInterval = setInterval(async () => {
const status = await piyangoKatilimciService.GetUploadJobStatus(jobGuid)
if (status) {
const idx = uploadJobs.value.findIndex((j: Record<string, any>) => j.guid === jobGuid)
if (idx >= 0) {
uploadJobs.value[idx] = { ...uploadJobs.value[idx], ...status }
}
if (status.status === 'Completed' || status.status === 'Failed') {
stopPolling()
piyangoKatilimciStore.refreshPiyangoKatilimciList = true
refreshUploadJobsAndPollProcessing()
}
}
}, 2000)
}
const refreshUploadJobsAndPollProcessing = async () => {
const cekilisId = piyangoStore.selectedLottery
if (cekilisId !== null) {
uploadJobs.value = await piyangoKatilimciService.GetUploadJobs(cekilisId)
startPollingForAnyProcessingJob()
}
}
const startPollingForAnyProcessingJob = () => {
const processingJob = uploadJobs.value.find(
(j: Record<string, any>) => j.status?.toLowerCase() === 'processing'
)
if (processingJob?.guid) {
startPollingForExistingJob(processingJob.guid)
}
}
onUnmounted(() => {
stopPolling()
removeUploadHandlers()
})
</script>
<style scoped>
.progress-bar { .progress-bar {
width: 90%; width: 90%;
max-width: 400px; max-width: 400px;
@ -384,6 +561,7 @@
font-weight: 600; font-weight: 600;
color: #333; color: #333;
} }
.upload-warning { .upload-warning {
background: #fff8db; background: #fff8db;
color: #856404; color: #856404;
@ -394,4 +572,164 @@
font-size: 14px; font-size: 14px;
text-align: center; text-align: center;
} }
.upload-panel-content {
display: flex;
flex-direction: column;
gap: 32px;
padding: 24px 24px 24px 24px;
}
.upload-progress-section {
background: #f0f9ff;
border: 1px solid #b6d4fe;
border-radius: 8px;
padding: 16px 20px;
display: flex;
flex-direction: column;
align-items: center;
}
.upload-progress-title {
margin: 0 0 12px;
font-size: 15px;
font-weight: 600;
color: #1a1a2e;
align-self: flex-start;
}
.upload-progress-section .upload-warning {
margin-bottom: 16px;
width: 100%;
}
.upload-progress-section .progress-bar {
width: 100%;
max-width: 100%;
}
.upload-progress-section .progress-text {
margin-top: 12px;
color: #1a1a2e;
}
.upload-history-section {
padding-bottom: 20px;
border-bottom: 1px solid #e8eaed;
}
.upload-history-title,
.upload-form-title {
margin: 0 0 14px;
font-size: 15px;
font-weight: 600;
color: #1a1a2e;
letter-spacing: 0.02em;
}
.upload-form-section {
padding-top: 4px;
}
.upload-form-section :deep(.panel-documents-item) {
margin-bottom: 0;
}
.upload-form-section :deep(.form-item-description) {
margin-top: 8px;
font-size: 13px;
color: #5f6368;
line-height: 1.4;
}
.history-loading {
text-align: center;
padding: 20px;
color: #5f6368;
font-size: 14px;
}
.history-items {
display: flex;
flex-direction: column;
gap: 8px;
}
.history-item {
display: grid;
grid-template-columns: 1fr auto auto auto;
align-items: center;
gap: 16px;
padding: 12px 16px;
border: 1px solid #e8eaed;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
background: #fafbfc;
}
.history-item:hover {
background: #f1f3f5;
border-color: #d0d5dd;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.history-file {
font-weight: 500;
font-size: 14px;
color: #1a1a2e;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.history-status {
font-size: 12px;
font-weight: 600;
padding: 4px 10px;
border-radius: 6px;
white-space: nowrap;
}
.history-status.status-completed {
color: #0d6832;
background: #d4edda;
}
.history-status.status-failed {
color: #721c24;
background: #f8d7da;
}
.history-status.status-processing {
color: #004085;
background: #cce5ff;
}
.history-status.status-pending {
color: #856404;
background: #fff3cd;
}
.history-progress {
font-size: 13px;
font-weight: 500;
color: #5f6368;
min-width: 36px;
text-align: right;
}
.history-date {
font-size: 12px;
color: #80868b;
min-width: 100px;
text-align: right;
}
/* İşleniyor durumunda progress bar */
.history-item.processing .history-progress {
font-weight: 600;
color: #007bff;
}
</style> </style>

View File

@ -80,6 +80,11 @@
sort: true, sort: true,
style: { width: '20%' } style: { width: '20%' }
}, },
{
name: 'cekilisHakkiAdedi',
title: 'Çekiliş Hakkı Adedi',
style: { width: '10%' }
},
{ {
name: 'sifreCode', name: 'sifreCode',

View File

@ -61,26 +61,26 @@
label="İzin Açıklaması" label="İzin Açıklaması"
@keyup="OnKeyup" /> @keyup="OnKeyup" />
</template> </template>
<template v-if="showIzinVerildiFields"> <form-select
<form-select v-if="showIzinVerildiFields || showMudurlukFields"
label="Müdürlük" label="Müdürlük"
:listData="mudurlukListesi" :listData="mudurlukListesi"
listText="name" listText="name"
listVal="id" listVal="id"
v-model="piyangoOnayStore.piyangoOnayForm.mudurlukId" v-model="piyangoOnayStore.piyangoOnayForm.mudurlukId"
required required
:invalidText="piyangoOnayValidationStore.invalidTexts.mudurlukId" :invalidText="piyangoOnayValidationStore.invalidTexts.mudurlukId"
@change="OnKeyup" /> @change="OnKeyup" />
<form-select <form-select
label="Çekiliş Görevlisi" v-if="showIzinVerildiFields"
:listData="cekilisGorevlisiListesi" label="Çekiliş Görevlisi"
listText="name" :listData="cekilisGorevlisiListesi"
listVal="id" listText="name"
v-model="piyangoOnayStore.piyangoOnayForm.cekilisGorevlisiId" listVal="id"
required v-model="piyangoOnayStore.piyangoOnayForm.cekilisGorevlisiId"
:invalidText="piyangoOnayValidationStore.invalidTexts.cekilisGorevlisiId" required
@change="OnKeyup" /> :invalidText="piyangoOnayValidationStore.invalidTexts.cekilisGorevlisiId"
</template> @change="OnKeyup" />
<form-select <form-select
v-if="showKapsamDisiSebebi" v-if="showKapsamDisiSebebi"
label="Kapsam Dışı Sebebi" label="Kapsam Dışı Sebebi"
@ -133,9 +133,11 @@
}) })
const showIzinVerildiFields = computed<boolean>(() => { const showIzinVerildiFields = computed<boolean>(() => {
return ( return piyangoOnayStore.piyangoOnayForm.onayDurumuIslemTipiId === 4
piyangoOnayStore.piyangoOnayForm.onayDurumuIslemTipiId === 4 })
)
const showMudurlukFields = computed<boolean>(() => {
return piyangoOnayStore.piyangoOnayForm.onayDurumuIslemTipiId === kapsamDisiId.value
}) })
const showKapsamDisiSebebi = computed<boolean>(() => { const showKapsamDisiSebebi = computed<boolean>(() => {

View File

@ -2,14 +2,14 @@
<list-table-content <list-table-content
v-if="loaded" v-if="loaded"
:tableHeader="tableHeader" :tableHeader="tableHeader"
:rowAction="EditOnay" :rowAction="adminRowAction"
formTitle="Piyango Onay Durumları" formTitle="Piyango Onay Durumları"
listText="Kayıt" listText="Kayıt"
:apiList="'OnayDurumu/GetSonOnayDurumlariList/' + piyangoStore.selectedLottery" :apiList="'OnayDurumu/GetSonOnayDurumlariList/' + piyangoStore.selectedLottery"
apiText="Piyango Onay Log Listesi" apiText="Piyango Onay Log Listesi"
page="form" page="form"
:refresh="piyangoOnayStore.refreshList" :refresh="piyangoOnayStore.refreshList"
:rowActions="rowActions" /> :rowActions="adminRowActions" />
<panel-wrapper <panel-wrapper
v-if="piyangoOnayStore.onayFormPanel" v-if="piyangoOnayStore.onayFormPanel"
v-model="piyangoOnayStore.onayFormPanel" v-model="piyangoOnayStore.onayFormPanel"
@ -45,6 +45,8 @@
const dialogStore = useDialogStore() const dialogStore = useDialogStore()
import { useDataStore } from '@/stores/dataStore' import { useDataStore } from '@/stores/dataStore'
const dataStore = useDataStore() const dataStore = useDataStore()
import { useUsersStore } from '@/stores/usersStore'
const usersStore = useUsersStore()
const loaded = ref<boolean>(false) const loaded = ref<boolean>(false)
@ -202,10 +204,7 @@
if (dt !== 'errorfalse') { if (dt !== 'errorfalse') {
dialogStore.CloseDialog('deleteonaydurumu') dialogStore.CloseDialog('deleteonaydurumu')
piyangoOnayStore.refreshList = false window.location.reload()
setTimeout(() => {
piyangoOnayStore.refreshList = true
}, 10)
} }
} }
@ -217,6 +216,14 @@
} }
]) ])
const adminRowAction = computed(() => {
return usersStore.isPanelUser ? EditOnay : undefined
})
const adminRowActions = computed(() => {
return usersStore.isPanelUser ? rowActions.value : []
})
onBeforeMount(async () => { onBeforeMount(async () => {
loaded.value = true loaded.value = true
}) })

View File

@ -0,0 +1,339 @@
<template>
<div class="panel-excel-upload-detail">
<div v-if="loading" class="detail-loading">Yükleniyor...</div>
<template v-else-if="job">
<div class="detail-summary">
<div class="detail-row detail-file-row">
<span class="detail-label">Dosya:</span>
<span class="detail-file-name">{{ job.fileName }}</span>
</div>
<div class="detail-row">
<span class="detail-label">Durum:</span>
<span :class="['detail-status', 'status-' + job.status?.toLowerCase()]">
{{ statusText(job.status) }}
</span>
</div>
<div class="detail-row">
<span class="detail-label">İlerleme:</span>
<span>{{ job.progress }}%</span>
</div>
<div class="detail-stats">
<div class="detail-stat-item">
<span class="detail-stat-value">{{ job.totalRows }}</span>
<span class="detail-stat-label">Toplam Satır</span>
</div>
<div class="detail-stat-item detail-stat-inserted">
<span class="detail-stat-value">{{ job.insertedCount ?? 0 }}</span>
<span class="detail-stat-label">Eklenen</span>
</div>
<div class="detail-stat-item detail-stat-skipped">
<span class="detail-stat-value">{{ job.skippedRows ?? 0 }}</span>
<span class="detail-stat-label">Atlanan</span>
</div>
<div class="detail-stat-item detail-stat-duplicate">
<span class="detail-stat-value">{{ job.duplicateCount ?? 0 }}</span>
<span class="detail-stat-label">Duplicate</span>
</div>
<div class="detail-stat-item detail-stat-error">
<span class="detail-stat-value">{{ job.errorCount ?? 0 }}</span>
<span class="detail-stat-label">Hata</span>
</div>
</div>
<div v-if="job.errorMessage" class="detail-error-box">
<span class="detail-error-label">Hata:</span>
<span>{{ job.errorMessage }}</span>
</div>
</div>
<div class="detail-logs" v-if="logs && logs.length > 0">
<h4 class="detail-logs-title">İşlem Logları</h4>
<div class="logs-list">
<div
v-for="log in logs"
:key="log.id"
:class="['log-item', 'log-' + log.logLevel?.toLowerCase()]">
<span :class="['log-level', 'log-level-' + log.logLevel?.toLowerCase()]">{{ log.logLevel }}</span>
<span v-if="log.rowNumber" class="log-row">Satır {{ log.rowNumber }}</span>
<span class="log-message">{{ log.message }}</span>
<span v-if="log.details" class="log-details">{{ log.details }}</span>
<span class="log-time">{{ formatDate(log.createdAt) }}</span>
</div>
</div>
</div>
<div v-else class="detail-no-logs">Log kaydı bulunamadı.</div>
</template>
<div v-else class="detail-not-found">Yükleme detayı bulunamadı.</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
import { usePiyangoKatilimciService } from '../../service/piyangoKatilimciService'
import { useDateStore } from '@/stores/dateStore'
const props = defineProps<{
jobGuid: string
}>()
const piyangoKatilimciService = usePiyangoKatilimciService()
const dateStore = useDateStore()
const loading = ref(true)
const job = ref<Record<string, any> | null>(null)
const logs = ref<Record<string, any>[]>([])
const statusText = (status: string) => {
const map: Record<string, string> = {
Pending: 'Bekliyor',
Processing: 'İşleniyor',
Completed: 'Tamamlandı',
Failed: 'Başarısız'
}
return map[status] || status
}
const formatDate = (val: string | Date) => {
if (!val) return '-'
return dateStore.dateFormat({ date: new Date(val), pattern: 'dd.mm.yyyy HH:MM' })
}
const loadDetail = async () => {
if (!props.jobGuid) return
loading.value = true
const data = await piyangoKatilimciService.GetUploadJobDetail(props.jobGuid)
if (data) {
job.value = data.job
logs.value = data.logs || []
} else {
job.value = null
logs.value = []
}
loading.value = false
}
onMounted(() => loadDetail())
watch(() => props.jobGuid, () => loadDetail())
</script>
<style scoped>
.panel-excel-upload-detail {
padding: 24px 24px 24px 24px;
}
.detail-loading {
text-align: center;
padding: 32px;
color: #5f6368;
font-size: 14px;
}
.detail-summary {
margin-bottom: 24px;
padding: 20px;
background: #fafbfc;
border: 1px solid #e8eaed;
border-radius: 8px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}
.detail-row {
display: grid;
grid-template-columns: 120px 1fr;
gap: 12px;
margin-bottom: 10px;
align-items: center;
}
.detail-file-row {
margin-bottom: 12px;
}
.detail-file-name {
font-weight: 600;
color: #1a1a2e;
word-break: break-all;
}
.detail-label {
font-weight: 600;
color: #5f6368;
font-size: 13px;
}
.detail-status {
font-size: 12px;
font-weight: 600;
padding: 4px 10px;
border-radius: 6px;
display: inline-block;
width: fit-content;
}
.detail-status.status-completed {
color: #0d6832;
background: #d4edda;
}
.detail-status.status-failed {
color: #721c24;
background: #f8d7da;
}
.detail-status.status-processing {
color: #004085;
background: #cce5ff;
}
.detail-status.status-pending {
color: #856404;
background: #fff3cd;
}
.detail-stats {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 12px;
margin: 16px 0;
padding: 16px 0;
border-top: 1px solid #e8eaed;
border-bottom: 1px solid #e8eaed;
}
.detail-stat-item {
text-align: center;
padding: 8px;
background: #f1f3f5;
border-radius: 6px;
}
.detail-stat-value {
display: block;
font-size: 18px;
font-weight: 700;
color: #1a1a2e;
}
.detail-stat-label {
font-size: 12px;
color: #5f6368;
}
.detail-error-box {
margin-top: 16px;
padding: 12px 16px;
background: #fff5f5;
border: 1px solid #f8d7da;
border-radius: 6px;
color: #721c24;
font-size: 13px;
display: flex;
gap: 8px;
}
.detail-error-label {
font-weight: 600;
flex-shrink: 0;
}
.detail-not-found {
padding: 24px;
text-align: center;
color: #721c24;
background: #fff5f5;
border: 1px solid #f8d7da;
border-radius: 8px;
font-weight: 500;
}
.detail-logs-title {
margin: 0 0 12px;
font-size: 15px;
font-weight: 600;
color: #1a1a2e;
}
.logs-list {
max-height: 400px;
overflow-y: auto;
border: 1px solid #e8eaed;
border-radius: 8px;
background: #fafbfc;
}
.log-item {
padding: 10px 14px;
border-bottom: 1px solid #e8eaed;
font-size: 13px;
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: baseline;
}
.log-item:last-child {
border-bottom: none;
}
.log-item.log-error {
background: #fff5f5;
}
.log-item.log-warning {
background: #fffbf0;
}
.log-item.log-info {
background: #f0f9ff;
}
.log-level {
font-weight: 600;
min-width: 60px;
font-size: 11px;
padding: 2px 8px;
border-radius: 4px;
}
.log-level.log-level-error {
background: #f8d7da;
color: #721c24;
}
.log-level.log-level-warning {
background: #fff3cd;
color: #856404;
}
.log-level.log-level-info {
background: #cce5ff;
color: #004085;
}
.log-row {
color: #5f6368;
font-family: ui-monospace, monospace;
font-size: 12px;
}
.log-message {
flex: 1;
min-width: 0;
}
.log-details {
font-size: 12px;
color: #5f6368;
width: 100%;
}
.log-time {
font-size: 11px;
color: #80868b;
margin-left: auto;
}
.detail-no-logs {
padding: 24px;
color: #80868b;
text-align: center;
font-style: italic;
font-size: 14px;
}
</style>

View File

@ -141,10 +141,10 @@
" "
half /> half />
<form-display <form-display
v-model="piyangoKatilimciStore.piyangoKatilimciUserFormData.cekilisKatilimSiraNo" v-model="piyangoKatilimciStore.piyangoKatilimciUserFormData.cekilisHakkiAdedi"
label="Katılım Sora No" label="Çekiliş Hakkı Adedi"
:invalidText=" :invalidText="
piyangoKatilimciValidationStore.userFormInvalidTexts.cekilisKatilimSiraNo piyangoKatilimciValidationStore.userFormInvalidTexts.cekilisHakkiAdedi
" "
half /> half />
<form-display <form-display
@ -287,6 +287,16 @@
:invalidText=" :invalidText="
piyangoKatilimciValidationStore.userFormInvalidTexts.cekilisKatilimSiraNo piyangoKatilimciValidationStore.userFormInvalidTexts.cekilisKatilimSiraNo
" /> " />
<form-input
@keyup="FormChanged"
modelKey="cekilisHakkiAdedi"
v-model="piyangoKatilimciStore.piyangoKatilimciUserFormData.cekilisHakkiAdedi"
half
label="Çekiliş Hakkı Adedi"
:invalidText="
piyangoKatilimciValidationStore.userFormInvalidTexts.cekilisHakkiAdedi
"
@keydown="validationStore.allowNumbersWithKeys" />
<form-input <form-input
@keyup="FormChanged" @keyup="FormChanged"
modelKey="magazaKartNumarasi" modelKey="magazaKartNumarasi"

View File

@ -50,6 +50,26 @@
label="İzin Açıklaması" label="İzin Açıklaması"
@keyup="OnKeyup" /> @keyup="OnKeyup" />
</template> </template>
<form-select
v-if="showIzinVerildiFields || showMudurlukFields"
label="Müdürlük"
:listData="mudurlukListesi"
listText="name"
listVal="id"
v-model="piyangoOnayStore.piyangoPanelOnayForm.mudurlukId"
required
:invalidText="piyangoOnayValidationStore.invalidTextsPanel.mudurlukId"
@change="OnKeyup" />
<form-select
v-if="showIzinVerildiFields"
label="Çekiliş Görevlisi"
:listData="cekilisGorevlisiListesi"
listText="name"
listVal="id"
v-model="piyangoOnayStore.piyangoPanelOnayForm.cekilisGorevlisiId"
required
:invalidText="piyangoOnayValidationStore.invalidTextsPanel.cekilisGorevlisiId"
@change="OnKeyup" />
<form-select <form-select
v-if="showKapsamDisiSebebi" v-if="showKapsamDisiSebebi"
label="Kapsam Dışı Sebebi" label="Kapsam Dışı Sebebi"
@ -98,6 +118,14 @@
) )
}) })
const showIzinVerildiFields = computed<boolean>(() => {
return piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId === 4
})
const showMudurlukFields = computed<boolean>(() => {
return piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId === kapsamDisiId.value
})
const showKapsamDisiSebebi = computed<boolean>(() => { const showKapsamDisiSebebi = computed<boolean>(() => {
return piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId === kapsamDisiId.value return piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId === kapsamDisiId.value
}) })
@ -114,6 +142,24 @@
{ id: 'Diğer', name: 'Diğer' } { id: 'Diğer', name: 'Diğer' }
]) ])
const cekilisGorevlisiListesi = ref<Record<string, any>[]>([
{ id: 1, name: 'Antalya Şube Müdürlüğü' },
{ id: 2, name: 'Gaziantep Şube Müdürlüğü' },
{ id: 3, name: 'Aksaray Şube Müdürlüğü' },
{ id: 4, name: 'Kadıköy Şube Müdürlüğü' },
{ id: 5, name: 'Karşıyaka Şube Müdürlüğü' },
{ id: 6, name: 'Muğla Şube Müdürlüğü' },
{ id: 7, name: 'Trabzon Şube Müdürlüğü' },
{ id: 8, name: 'Noter' },
{ id: 9, name: 'Başkanlık Personeli' }
])
const mudurlukListesi = ref<Record<string, any>[]>([
{ id: 1, name: '1 No\'lu Özel Çekilişler İzin ve Takip Şubesi Müdürlüğü' },
{ id: 2, name: '2 No\'lu Özel Çekilişler İzin ve Takip Şubesi Müdürlüğü' },
{ id: 3, name: '3 No\'lu Özel Çekilişler İzin ve Takip Şubesi Müdürlüğü' }
])
const OnKeyup = () => { const OnKeyup = () => {
piyangoOnayValidationStore.formChanged = true piyangoOnayValidationStore.formChanged = true
} }

View File

@ -15,6 +15,10 @@ export const usePiyangoKatilimciService = defineStore('piyangoKatilimciService',
const SaveKatilimciUser = async () => { const SaveKatilimciUser = async () => {
if (piyangoKatilimciValidationStore.UserFormCheck()) { if (piyangoKatilimciValidationStore.UserFormCheck()) {
let form: any let form: any
piyangoKatilimciStore.piyangoKatilimciUserFormData.cekilisHakkiAdedi = Math.max(
Number(piyangoKatilimciStore.piyangoKatilimciUserFormData.cekilisHakkiAdedi) || 1,
1
)
if (!piyangoKatilimciStore.isPiyangoKatilimciUserUpdate) { if (!piyangoKatilimciStore.isPiyangoKatilimciUserUpdate) {
delete piyangoKatilimciStore.piyangoKatilimciUserFormData.id delete piyangoKatilimciStore.piyangoKatilimciUserFormData.id
@ -82,5 +86,46 @@ export const usePiyangoKatilimciService = defineStore('piyangoKatilimciService',
} }
} }
return { SaveKatilimciUser, CreateOnlineDraw, KatilimciData, KatilimciFileUpload } const GetUploadJobs = async (cekilisId: number): Promise<Record<string, any>[]> => {
const dt = await dataStore.dataGet(`ExcelUploadJob/my-jobs?cekilisId=${cekilisId}`)
if (dt !== 'errorfalse' && Array.isArray(dt)) {
return dt
}
return []
}
const GetUploadJobDetail = async (guid: string): Promise<Record<string, any> | null> => {
const dt = await dataStore.dataGet(`ExcelUploadJob/${guid}`)
if (dt !== 'errorfalse') {
return dt
}
return null
}
const GetUploadJobStatus = async (guid: string): Promise<Record<string, any> | null> => {
const dt = await dataStore.dataGet(`ExcelUploadJob/status/${guid}`)
if (dt !== 'errorfalse') {
return dt
}
return null
}
const SubscribeToJob = async (guid: string, connectionId: string): Promise<boolean> => {
const result = await dataStore.dataPost(
`ExcelUploadJob/subscribe?guid=${guid}&connectionId=${encodeURIComponent(connectionId)}`,
{}
)
return result !== 'errorfalse'
}
return {
SaveKatilimciUser,
CreateOnlineDraw,
KatilimciData,
KatilimciFileUpload,
GetUploadJobs,
GetUploadJobDetail,
GetUploadJobStatus,
SubscribeToJob
}
}) })

View File

@ -39,8 +39,13 @@ export const usePiyangoOnayService = defineStore('piyangoOnayService', () => {
if (piyangoOnayStore.piyangoOnayForm.onayDurumuIslemTipiId !== getKapsamDisiId()) { if (piyangoOnayStore.piyangoOnayForm.onayDurumuIslemTipiId !== getKapsamDisiId()) {
piyangoOnayStore.piyangoOnayForm.kapsamDisiSebebi = null piyangoOnayStore.piyangoOnayForm.kapsamDisiSebebi = null
} }
if (piyangoOnayStore.piyangoOnayForm.onayDurumuIslemTipiId !== 4) { if (
piyangoOnayStore.piyangoOnayForm.onayDurumuIslemTipiId !== 4 &&
piyangoOnayStore.piyangoOnayForm.onayDurumuIslemTipiId !== getKapsamDisiId()
) {
piyangoOnayStore.piyangoOnayForm.mudurlukId = null piyangoOnayStore.piyangoOnayForm.mudurlukId = null
}
if (piyangoOnayStore.piyangoOnayForm.onayDurumuIslemTipiId !== 4) {
piyangoOnayStore.piyangoOnayForm.cekilisGorevlisiId = null piyangoOnayStore.piyangoOnayForm.cekilisGorevlisiId = null
} }
dataForm.append( dataForm.append(
@ -88,11 +93,8 @@ export const usePiyangoOnayService = defineStore('piyangoOnayService', () => {
piyangoOnayValidationStore.formChanged = false piyangoOnayValidationStore.formChanged = false
piyangoStore.lotteryApprove = piyangoStore.lotteryApprove =
piyangoOnayStore.piyangoOnayForm.onayDurumuIslemTipiId piyangoOnayStore.piyangoOnayForm.onayDurumuIslemTipiId
piyangoOnayStore.refreshList = true
piyangoOnayStore.ResetForm() piyangoOnayStore.ResetForm()
setTimeout(() => { window.location.reload()
piyangoOnayStore.loaded = true
}, 10)
} }
} else { } else {
piyangoOnayValidationStore.isFormValid = true piyangoOnayValidationStore.isFormValid = true
@ -112,6 +114,15 @@ export const usePiyangoOnayService = defineStore('piyangoOnayService', () => {
if (piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId !== getKapsamDisiId()) { if (piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId !== getKapsamDisiId()) {
piyangoOnayStore.piyangoPanelOnayForm.kapsamDisiSebebi = null piyangoOnayStore.piyangoPanelOnayForm.kapsamDisiSebebi = null
} }
if (
piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId !== 4 &&
piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId !== getKapsamDisiId()
) {
piyangoOnayStore.piyangoPanelOnayForm.mudurlukId = null
}
if (piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId !== 4) {
piyangoOnayStore.piyangoPanelOnayForm.cekilisGorevlisiId = null
}
dataForm.append('onayDurumuIslemTipiId', piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId) dataForm.append('onayDurumuIslemTipiId', piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId)
dataForm.append('id', piyangoOnayStore.piyangoPanelOnayForm.id) dataForm.append('id', piyangoOnayStore.piyangoPanelOnayForm.id)
dataForm.append('onayCekilisId', String(piyangoStore.selectedLottery)) dataForm.append('onayCekilisId', String(piyangoStore.selectedLottery))
@ -132,6 +143,18 @@ export const usePiyangoOnayService = defineStore('piyangoOnayService', () => {
? String(piyangoOnayStore.piyangoPanelOnayForm.kapsamDisiSebebi) ? String(piyangoOnayStore.piyangoPanelOnayForm.kapsamDisiSebebi)
: '' : ''
) )
dataForm.append(
'mudurluk',
piyangoOnayStore.piyangoPanelOnayForm.mudurlukId != null
? String(piyangoOnayStore.piyangoPanelOnayForm.mudurlukId)
: ''
)
dataForm.append(
'cekilisGorevlisi',
piyangoOnayStore.piyangoPanelOnayForm.cekilisGorevlisiId != null
? String(piyangoOnayStore.piyangoPanelOnayForm.cekilisGorevlisiId)
: ''
)
form = await dataStore.dataPut( form = await dataStore.dataPut(
'OnayDurumu/' + piyangoOnayStore.piyangoPanelOnayForm.id, 'OnayDurumu/' + piyangoOnayStore.piyangoPanelOnayForm.id,

View File

@ -1,48 +1,68 @@
import * as signalR from '@microsoft/signalr' import * as signalR from '@microsoft/signalr'
let connection: signalR.HubConnection let connection: signalR.HubConnection | null = null
let connectionId = ''
export const connectToHub = async () => { export const connectToHub = async (): Promise<string> => {
console.log('Connecting to SignalR Hub...') console.log('Connecting to SignalR Hub...')
// Mevcut bağlantı varsa kapat
if (connection && connection.state === signalR.HubConnectionState.Connected) { // Mevcut bağlantı varsa her durumda kapat (Connected, Reconnecting, Disconnected)
await connection.stop() if (connection) {
try {
await connection.stop()
} catch {
// Bağlantı zaten kapalı olabilir, yoksay
}
connection = null
} }
connection = new signalR.HubConnectionBuilder() connection = new signalR.HubConnectionBuilder()
.withUrl(import.meta.env.VITE_SOCKET_URL, { .withUrl(import.meta.env.VITE_SOCKET_URL, {
withCredentials: false, withCredentials: false,
skipNegotiation: true, // WebSocket kullanırken negotiation atlanabilir skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets transport: signalR.HttpTransportType.WebSockets
}) })
.withAutomaticReconnect() .withAutomaticReconnect()
.build() .build()
// Eventleri ekle // Eventleri ekle (connection artık tanımlı)
onProgress((data) => console.log('progress', data)) connection.on('ReceiveProgress', (data: any) => console.log('progress', data))
onInsertProgress((data) => console.log('insert progress', data)) connection.on('ReceiveInsertProgress', (data: any) => console.log('insert progress', data))
onCompleted((data) => console.log('completed', data)) connection.on('ReceiveCompleted', (data: any) => console.log('completed', data))
onError((data) => console.log('error', data)) connection.on('ReceiveError', (data: any) => console.log('error', data))
await connection.start() try {
connectionId = await connection.invoke<string>('GetConnectionId') await connection.start()
console.log('Connected to SignalR Hub with Connection ID:', connectionId) const connectionId = await connection.invoke<string>('GetConnectionId')
return connectionId console.log('Connected to SignalR Hub with Connection ID:', connectionId)
return connectionId
} catch (err) {
connection = null
throw err
}
} }
export const onProgress = (callback: (data: any) => void) => { export const onProgress = (callback: (data: any) => void) => {
connection.on('ReceiveProgress', callback) if (connection) connection.on('ReceiveProgress', callback)
} }
export const onInsertProgress = (callback: (data: any) => void) => { export const onInsertProgress = (callback: (data: any) => void) => {
connection.on('ReceiveInsertProgress', callback) if (connection) connection.on('ReceiveInsertProgress', callback)
} }
export const onCompleted = (callback: (data: any) => void) => { export const onCompleted = (callback: (data: any) => void) => {
connection.on('ReceiveCompleted', callback) if (connection) connection.on('ReceiveCompleted', callback)
} }
export const onError = (callback: (data: any) => void) => { export const onError = (callback: (data: any) => void) => {
connection.on('ReceiveError', callback) if (connection) connection.on('ReceiveError', callback)
}
/** Upload event handler'larını kaldır (memory leak ve çoklu tetikleme önleme) */
export const removeUploadHandlers = () => {
if (connection) {
connection.off('ReceiveProgress')
connection.off('ReceiveInsertProgress')
connection.off('ReceiveCompleted')
connection.off('ReceiveError')
}
} }

View File

@ -10,6 +10,7 @@ export const usePiyangoKatilimciStore = defineStore('piyangoKatilimciStore', ()
const piyangoKatilimciUserSafeFormData = reactive<Record<string, any>>({ const piyangoKatilimciUserSafeFormData = reactive<Record<string, any>>({
ikramiyeId: null, ikramiyeId: null,
cekilisKatilimSiraNo: null, cekilisKatilimSiraNo: null,
cekilisHakkiAdedi: 1,
sifreCode: '', sifreCode: '',
magazaKartNumarasi: '', magazaKartNumarasi: '',
adi: '', adi: '',

View File

@ -96,6 +96,31 @@ export const usePiyangoKatilimciValidationStore = defineStore(
'Lütfen soyadını giriniz.' 'Lütfen soyadını giriniz.'
) )
validationStore.IsFieldEmpty(
piyangoKatilimciStore.piyangoKatilimciUserFormData,
userFormInvalidTexts,
'cekilisHakkiAdedi',
'Lütfen çekiliş hakkı adedini giriniz.'
)
if (
!validationStore.checkEmpty(
piyangoKatilimciStore.piyangoKatilimciUserFormData.cekilisHakkiAdedi
)
) {
const hakAdedi = Number(
piyangoKatilimciStore.piyangoKatilimciUserFormData.cekilisHakkiAdedi
)
if (!Number.isInteger(hakAdedi) || hakAdedi < 1) {
userFormInvalidTexts.cekilisHakkiAdedi =
'Çekiliş hakkı adedi en az 1 olmalıdır.'
isUserFormValid.value = false
} else {
delete userFormInvalidTexts.cekilisHakkiAdedi
}
}
var today: Date | string = new Date() var today: Date | string = new Date()
today = dateStore.dateFormat({ today = dateStore.dateFormat({
date: today, date: today,

View File

@ -51,13 +51,18 @@ export const usePiyangoOnayValidationStore = defineStore(
'İzin tarihi seçmelisiniz.' 'İzin tarihi seçmelisiniz.'
) )
} }
if (piyangoOnayStore.piyangoOnayForm.onayDurumuIslemTipiId === 4) { if (
piyangoOnayStore.piyangoOnayForm.onayDurumuIslemTipiId === 4 ||
piyangoOnayStore.piyangoOnayForm.onayDurumuIslemTipiId === kapsamDisiId.value
) {
validationStore.IsFieldEmpty( validationStore.IsFieldEmpty(
piyangoOnayStore.piyangoOnayForm, piyangoOnayStore.piyangoOnayForm,
invalidTexts, invalidTexts,
'mudurlukId', 'mudurlukId',
'Müdürlük seçmelisiniz.' 'Müdürlük seçmelisiniz.'
) )
}
if (piyangoOnayStore.piyangoOnayForm.onayDurumuIslemTipiId === 4) {
validationStore.IsFieldEmpty( validationStore.IsFieldEmpty(
piyangoOnayStore.piyangoOnayForm, piyangoOnayStore.piyangoOnayForm,
invalidTexts, invalidTexts,
@ -100,6 +105,25 @@ export const usePiyangoOnayValidationStore = defineStore(
'İzin tarihi seçmelisiniz.' 'İzin tarihi seçmelisiniz.'
) )
} }
if (
piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId === 4 ||
piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId === kapsamDisiId.value
) {
validationStore.IsFieldEmpty(
piyangoOnayStore.piyangoPanelOnayForm,
invalidTextsPanel,
'mudurlukId',
'Müdürlük seçmelisiniz.'
)
}
if (piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId === 4) {
validationStore.IsFieldEmpty(
piyangoOnayStore.piyangoPanelOnayForm,
invalidTextsPanel,
'cekilisGorevlisiId',
'Çekiliş görevlisi seçmelisiniz.'
)
}
if (piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId === kapsamDisiId.value) { if (piyangoOnayStore.piyangoPanelOnayForm.onayDurumuIslemTipiId === kapsamDisiId.value) {
validationStore.IsFieldEmpty( validationStore.IsFieldEmpty(
piyangoOnayStore.piyangoPanelOnayForm, piyangoOnayStore.piyangoPanelOnayForm,

View File

@ -29,7 +29,7 @@
</template> </template>
<template <template
#dosya-kapama #dosya-kapama
v-if="usersStore.isPanelUser && piyangoStore.lotteryDrawState"> v-if="piyangoStore.lotteryApprove === 10">
<tab-piyango-dosya-kapama /> <tab-piyango-dosya-kapama />
</template> </template>
</tabs> </tabs>
@ -100,7 +100,7 @@
tabList.value.push({ text: 'Teminat Listesi', id: 'teminat-listesi' }) tabList.value.push({ text: 'Teminat Listesi', id: 'teminat-listesi' })
} }
if (usersStore.isPanelUser && piyangoStore.lotteryDrawState) { if (piyangoStore.lotteryApprove === 10) {
tabList.value.push({ text: 'Dosya Kapama', id: 'dosya-kapama' }) tabList.value.push({ text: 'Dosya Kapama', id: 'dosya-kapama' })
} }
} }

View File

@ -78,7 +78,58 @@
style: { width: '10%' } style: { width: '10%' }
}) })
// 2. MÜDÜRLÜK // 2. OLUŞTURMA TARİHİ
header.push({
name: 'olusturmaTarihi',
title: 'Oluşturma Tarihi',
compute: (v: Record<string, any>): string => {
if (!v.olusturmaTarihi || v.olusturmaTarihi === null || v.olusturmaTarihi.includes('0001-')) return ''
return dateStore.dateFormat({ date: v.olusturmaTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
},
sort: true,
filter: {
type: 'date',
between: true
}
})
// 3. SEVK DURUMU (Panel User için)
if (usersStore.isPanelUser) {
header.push({
name: 'atanmis',
title: 'Sevk Durumu',
computeHtml: (v: Record<string, any>): string => {
if (v.atanmis) {
return `<strong class="back-grad back-grad-sevk-ok">
${v.atananlar}
</strong>`
} else {
return `<span class="back-grad back-grad-sevk">
Sevk Edilmemiş</span>`
}
}
})
// 4. MUHASEBELEŞTİRME DURUMU (Panel User için)
header.push({
name: 'basvuruBedelNo',
title: 'Muhasebeleştirme Durumu',
computeHtml: (v: Record<string, any>): string => {
let durum = ''
if (v.basvuruBedelNo !== null) {
durum += `<strong">Başvuru Bedel No: </strong>
${v.basvuruBedelNo}<br>`
}
if (v.izinBedelNo !== null) {
durum += `<strong">İzin Bedel No: </strong>
${v.izinBedelNo}`
}
return durum
}
})
}
// 5. MÜDÜRLÜK
header.push({ header.push({
name: 'mudurluk', name: 'mudurluk',
title: 'Müdürlük', title: 'Müdürlük',
@ -90,7 +141,7 @@
} }
}) })
// 3. PİYANGO AMACI // 6. PİYANGO AMACI
header.push({ header.push({
name: 'piyangoamac', name: 'piyangoamac',
title: 'Piyango Amacı', title: 'Piyango Amacı',
@ -104,7 +155,7 @@
} }
}) })
// 4. DÜZENLEYEN (koşullu) // 7. DÜZENLEYEN (koşullu)
if (usersStore.isAraciFirma || usersStore.isPanelUser) { if (usersStore.isAraciFirma || usersStore.isPanelUser) {
header.push({ header.push({
name: 'duzenleyen', name: 'duzenleyen',
@ -115,10 +166,10 @@
}) })
} }
// 5. ARACI FİRMA // 8. ARACI FİRMA
header.push({ header.push({
name: 'araciFirma', name: 'araciFirma',
title: 'Araci Firma', title: 'Aracı Firma',
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
return v.araciFirma || '' return v.araciFirma || ''
}, },
@ -152,7 +203,7 @@
title: 'İzin Tarihi', title: 'İzin Tarihi',
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
if (!v.izinTarihi || v.izinTarihi === null) return '' if (!v.izinTarihi || v.izinTarihi === null) return ''
return dateStore.dateFormat({ date: v.izinTarihi }) return dateStore.dateFormat({ date: v.izinTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
}, },
sort: true, sort: true,
filter: { filter: {
@ -181,7 +232,7 @@
title: 'Başlangıç Tarihi', title: 'Başlangıç Tarihi',
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
if (!v.baslangicTarihi || v.baslangicTarihi.includes('0001-')) return '' if (!v.baslangicTarihi || v.baslangicTarihi.includes('0001-')) return ''
return dateStore.dateFormat({ date: v.baslangicTarihi }) return dateStore.dateFormat({ date: v.baslangicTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
}, },
sort: true, sort: true,
filter: { filter: {
@ -196,7 +247,7 @@
title: 'Bitiş Tarihi', title: 'Bitiş Tarihi',
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
if (!v.bitisTarihi || v.bitisTarihi.includes('0001-')) return '' if (!v.bitisTarihi || v.bitisTarihi.includes('0001-')) return ''
return dateStore.dateFormat({ date: v.bitisTarihi }) return dateStore.dateFormat({ date: v.bitisTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
}, },
sort: true, sort: true,
filter: { filter: {
@ -212,7 +263,7 @@
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
if (!v.cekilisTarihi || v.cekilisTarihi.includes('0001-')) return '' if (!v.cekilisTarihi || v.cekilisTarihi.includes('0001-')) return ''
if (v.piyangoAmacId === 3) return '' if (v.piyangoAmacId === 3) return ''
return dateStore.dateFormat({ date: v.cekilisTarihi }) return dateStore.dateFormat({ date: v.cekilisTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
}, },
sort: true, sort: true,
filter: { filter: {
@ -239,7 +290,7 @@
title: 'Gazete İlan Tarihi', title: 'Gazete İlan Tarihi',
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
if (!v.gazeteIlanTarihi || v.gazeteIlanTarihi === null) return '' if (!v.gazeteIlanTarihi || v.gazeteIlanTarihi === null) return ''
return dateStore.dateFormat({ date: v.gazeteIlanTarihi }) return dateStore.dateFormat({ date: v.gazeteIlanTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
}, },
sort: true, sort: true,
filter: { filter: {
@ -285,7 +336,7 @@
title: 'Teminat Tarihi', title: 'Teminat Tarihi',
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
if (!v.teminatTarihi || v.teminatTarihi === null) return '' if (!v.teminatTarihi || v.teminatTarihi === null) return ''
return dateStore.dateFormat({ date: v.teminatTarihi }) return dateStore.dateFormat({ date: v.teminatTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
}, },
sort: true sort: true
}) })
@ -335,7 +386,7 @@
title: 'T. İade Tarihi', title: 'T. İade Tarihi',
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
if (!v.teminatIadeTarihi || v.teminatIadeTarihi === null) return '' if (!v.teminatIadeTarihi || v.teminatIadeTarihi === null) return ''
return dateStore.dateFormat({ date: v.teminatIadeTarihi }) return dateStore.dateFormat({ date: v.teminatIadeTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
}, },
sort: true sort: true
}) })
@ -360,41 +411,6 @@
} }
}) })
// Panel User için ek sütunlar
if (usersStore.isPanelUser) {
header.push({
name: 'atanmis',
title: 'Sevk Durumu',
computeHtml: (v: Record<string, any>): string => {
if (v.atanmis) {
return `<strong class="back-grad back-grad-sevk-ok">
${v.atananlar}
</strong>`
} else {
return `<span class="back-grad back-grad-sevk">
Sevk Edilmemiş</span>`
}
}
})
header.push({
name: 'basvuruBedelNo',
title: 'Muhasebeleştirme Durumu',
computeHtml: (v: Record<string, any>): string => {
let durum = ''
if (v.basvuruBedelNo !== null) {
durum += `<strong">Başvuru Bedel No: </strong>
${v.basvuruBedelNo}<br>`
}
if (v.izinBedelNo !== null) {
durum += `<strong">İzin Bedel No: </strong>
${v.izinBedelNo}`
}
return durum
}
})
}
return header return header
}) })

View File

@ -289,6 +289,7 @@
'success', 'success',
30000 30000
) )
dialogStore.CloseDialog('basvurubedelimuhasebelestir')
loaded.value = false loaded.value = false
await GetData() await GetData()
piyangoStore.lotteryBasvuruBedeliStatus = true piyangoStore.lotteryBasvuruBedeliStatus = true

View File

@ -9,7 +9,7 @@
icon="draws" icon="draws"
title="Piyangolar" title="Piyangolar"
listText="Piyango" listText="Piyango"
:apiList="'Cekilis/GetCekilislerListAtanan/' + usersStore.userId" :apiList="'Cekilis/GetCekilislerListMuhasebe/'"
apiText="Piyango Listesi" apiText="Piyango Listesi"
isUseRoute /> isUseRoute />
</section> </section>
@ -60,7 +60,56 @@
style: { width: '10%' } style: { width: '10%' }
}) })
// 2. MÜDÜRLÜK // 2. OLUŞTURMA TARİHİ
header.push({
name: 'olusturmaTarihi',
title: 'Oluşturma Tarihi',
compute: (v: Record<string, any>): string => {
if (!v.olusturmaTarihi || v.olusturmaTarihi === null || v.olusturmaTarihi.includes('0001-')) return ''
return dateStore.dateFormat({ date: v.olusturmaTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
},
sort: true,
filter: {
type: 'date',
between: true
}
})
// 3. SEVK DURUMU
header.push({
name: 'atanmis',
title: 'Sevk Durumu',
computeHtml: (v: Record<string, any>): string => {
if (v.atanmis) {
return `<strong class="back-grad back-grad-sevk-ok">
${v.atananlar}
</strong>`
} else {
return `<span class="back-grad back-grad-sevk">
Sevk Edilmemiş</span>`
}
}
})
// 4. MUHASEBELEŞTİRME DURUMU
header.push({
name: 'basvuruBedelNo',
title: 'Muhasebeleştirme Durumu',
computeHtml: (v: Record<string, any>): string => {
let durum = ''
if (v.basvuruBedelNo !== null) {
durum += `<strong">Başvuru Bedel No: </strong>
${v.basvuruBedelNo}<br>`
}
if (v.izinBedelNo !== null) {
durum += `<strong">İzin Bedel No: </strong>
${v.izinBedelNo}`
}
return durum
}
})
// 5. MÜDÜRLÜK
header.push({ header.push({
name: 'mudurluk', name: 'mudurluk',
title: 'Müdürlük', title: 'Müdürlük',
@ -69,7 +118,7 @@
} }
}) })
// 3. PİYANGO AMACI // 6. PİYANGO AMACI
header.push({ header.push({
name: 'piyangoamac', name: 'piyangoamac',
title: 'Piyango Amacı', title: 'Piyango Amacı',
@ -83,15 +132,15 @@
} }
}) })
// 4. DÜZENLEYEN (koşullu) // 7. DÜZENLEYEN (koşullu)
if (usersStore.isAraciFirma || usersStore.isPanelUser) { if (usersStore.isAraciFirma || usersStore.isPanelUser) {
header.push({ name: 'duzenleyen', title: 'Düzenleyen' }) header.push({ name: 'duzenleyen', title: 'Düzenleyen' })
} }
// 5. ARACI FİRMA // 8. ARACI FİRMA
header.push({ header.push({
name: 'araciFirma', name: 'araciFirma',
title: 'Araci Firma', title: 'Aracı Firma',
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
return v.araciFirma || '' return v.araciFirma || ''
} }
@ -122,7 +171,7 @@
title: 'İzin Tarihi', title: 'İzin Tarihi',
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
if (!v.izinTarihi || v.izinTarihi === null) return '' if (!v.izinTarihi || v.izinTarihi === null) return ''
return dateStore.dateFormat({ date: v.izinTarihi }) return dateStore.dateFormat({ date: v.izinTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
}, },
sort: true, sort: true,
filter: { filter: {
@ -147,7 +196,7 @@
title: 'Başlangıç Tarihi', title: 'Başlangıç Tarihi',
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
if (!v.baslangicTarihi || v.baslangicTarihi.includes('0001-')) return '' if (!v.baslangicTarihi || v.baslangicTarihi.includes('0001-')) return ''
return dateStore.dateFormat({ date: v.baslangicTarihi }) return dateStore.dateFormat({ date: v.baslangicTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
}, },
sort: true, sort: true,
filter: { filter: {
@ -162,7 +211,7 @@
title: 'Bitiş Tarihi', title: 'Bitiş Tarihi',
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
if (!v.bitisTarihi || v.bitisTarihi.includes('0001-')) return '' if (!v.bitisTarihi || v.bitisTarihi.includes('0001-')) return ''
return dateStore.dateFormat({ date: v.bitisTarihi }) return dateStore.dateFormat({ date: v.bitisTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
}, },
sort: true, sort: true,
filter: { filter: {
@ -178,7 +227,7 @@
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
if (!v.cekilisTarihi || v.cekilisTarihi.includes('0001-')) return '' if (!v.cekilisTarihi || v.cekilisTarihi.includes('0001-')) return ''
if (v.piyangoAmacId === 3) return '' if (v.piyangoAmacId === 3) return ''
return dateStore.dateFormat({ date: v.cekilisTarihi }) return dateStore.dateFormat({ date: v.cekilisTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
}, },
sort: true, sort: true,
filter: { filter: {
@ -187,6 +236,7 @@
} }
}) })
// 12. ÇEKİLİŞ GÖREVLİSİ // 12. ÇEKİLİŞ GÖREVLİSİ
header.push({ header.push({
name: 'cekilisGorevlisi', name: 'cekilisGorevlisi',
@ -202,7 +252,7 @@
title: 'Gazete İlan Tarihi', title: 'Gazete İlan Tarihi',
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
if (!v.gazeteIlanTarihi || v.gazeteIlanTarihi === null) return '' if (!v.gazeteIlanTarihi || v.gazeteIlanTarihi === null) return ''
return dateStore.dateFormat({ date: v.gazeteIlanTarihi }) return dateStore.dateFormat({ date: v.gazeteIlanTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
}, },
sort: true, sort: true,
filter: { filter: {
@ -248,7 +298,7 @@
title: 'Teminat Tarihi', title: 'Teminat Tarihi',
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
if (!v.teminatTarihi || v.teminatTarihi === null) return '' if (!v.teminatTarihi || v.teminatTarihi === null) return ''
return dateStore.dateFormat({ date: v.teminatTarihi }) return dateStore.dateFormat({ date: v.teminatTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
}, },
sort: true sort: true
}) })
@ -298,7 +348,7 @@
title: 'T. İade Tarihi', title: 'T. İade Tarihi',
compute: (v: Record<string, any>): string => { compute: (v: Record<string, any>): string => {
if (!v.teminatIadeTarihi || v.teminatIadeTarihi === null) return '' if (!v.teminatIadeTarihi || v.teminatIadeTarihi === null) return ''
return dateStore.dateFormat({ date: v.teminatIadeTarihi }) return dateStore.dateFormat({ date: v.teminatIadeTarihi, pattern: 'dd-mm-yy', splitDate: '/' })
}, },
sort: true sort: true
}) })
@ -323,38 +373,6 @@
} }
}) })
// Muhasebe özel sütunlar
header.push({
name: 'atanmis',
title: 'Sevk Durumu',
computeHtml: (v: Record<string, any>): string => {
if (v.atanmis) {
return `<strong class="back-grad back-grad-sevk-ok">
${v.atananlar}
</strong>`
} else {
return `<span class="back-grad back-grad-sevk">
Sevk Edilmemiş</span>`
}
}
})
header.push({
name: 'basvuruBedelNo',
title: 'Muhasebeleştirme Durumu',
computeHtml: (v: Record<string, any>): string => {
let durum = ''
if (v.basvuruBedelNo !== null) {
durum += `<strong">Başvuru Bedel No: </strong>
${v.basvuruBedelNo}<br>`
}
if (v.izinBedelNo !== null) {
durum += `<strong">İzin Bedel No: </strong>
${v.izinBedelNo}`
}
return durum
}
})
return header return header
}) })

View File

@ -49,7 +49,7 @@ export const useDataStore = defineStore('dataStore', () => {
return response.data return response.data
} }
} catch (error: any) { } catch (error: any) {
CheckApiError(error.response.status, error.response.data) CheckApiError(error.response?.status, error.response?.data)
console.error('Hata oluştu -:', error) console.error('Hata oluştu -:', error)
return 'errorfalse' return 'errorfalse'
} }
@ -64,6 +64,8 @@ export const useDataStore = defineStore('dataStore', () => {
options?: Record<string, any> options?: Record<string, any>
toast?: Record<string, any> toast?: Record<string, any>
full?: boolean full?: boolean
/** Bu status kodları için CheckApiError atlanır, hata objesi döner */
skipErrorForStatuses?: number[]
} = {} } = {}
): Promise<any> => { ): Promise<any> => {
try { try {
@ -86,8 +88,11 @@ export const useDataStore = defineStore('dataStore', () => {
return response.data return response.data
} }
} catch (error: any) { } catch (error: any) {
CheckApiError(error.response.status, error.response.data) const status = error.response?.status
if (data.skipErrorForStatuses?.includes(status)) {
return { _error: true, status, data: error.response?.data }
}
CheckApiError(status, error.response?.data)
console.error('Hata oluştu:', error) console.error('Hata oluştu:', error)
return Promise.resolve('errorfalse') return Promise.resolve('errorfalse')
} }
@ -124,8 +129,7 @@ export const useDataStore = defineStore('dataStore', () => {
return response.data return response.data
} }
} catch (error: any) { } catch (error: any) {
CheckApiError(error.response.status, error.response.data) CheckApiError(error.response?.status, error.response?.data)
console.error('Hata oluştu:', error) console.error('Hata oluştu:', error)
return Promise.resolve('errorfalse') return Promise.resolve('errorfalse')
} }
@ -161,16 +165,20 @@ export const useDataStore = defineStore('dataStore', () => {
return response.data return response.data
} }
} catch (error: any) { } catch (error: any) {
CheckApiError(error.response.status, error.response.data) CheckApiError(error.response?.status, error.response?.data)
console.error('Hata oluştu:', error) console.error('Hata oluştu:', error)
return Promise.resolve('errorfalse') return Promise.resolve('errorfalse')
} }
} }
const CheckApiError = async (status: number, data: Record<string, any>) => { const CheckApiError = (status?: number, data?: Record<string, any>) => {
if (status === undefined) {
toastStore.AddToast("Bağlantı hatası. İnternet bağlantınızı kontrol edin.", "alert", 8000);
return;
}
if (status === 401) return; // Axios interceptor login'e yönlendirir
if (status === 400) { if (status === 400) {
const errorKey = typeof data === "string" ? data : data?.hata || data?.errors; const errorKey = typeof data === "string" ? data : data?.hata || data?.errors;
if (errorKey !== undefined) { if (errorKey !== undefined) {
if (Array.isArray(errorKey)) { if (Array.isArray(errorKey)) {
errorKey.forEach((el: string) => { errorKey.forEach((el: string) => {
@ -184,7 +192,19 @@ export const useDataStore = defineStore('dataStore', () => {
} else { } else {
toastStore.AddToast("Bir hata oluştu.", "alert"); toastStore.AddToast("Bir hata oluştu.", "alert");
} }
return;
} }
const httpErrorMessages: Record<number, string> = {
413: "Dosya boyutu çok büyük. Lütfen daha küçük bir dosya yükleyin veya sunucu limitlerini kontrol edin.",
404: "İstenen kaynak bulunamadı.",
500: "Sunucu hatası oluştu. Lütfen daha sonra tekrar deneyin.",
502: "Sunucu geçici olarak yanıt vermiyor. Lütfen daha sonra tekrar deneyin.",
503: "Servis şu an kullanılamıyor. Lütfen daha sonra tekrar deneyin.",
504: "İstek zaman aşımına uğradı. Lütfen tekrar deneyin.",
408: "İstek zaman aşımına uğradı. Lütfen tekrar deneyin."
};
const message = httpErrorMessages[status] || `Beklenmeyen hata (${status}). Lütfen tekrar deneyin.`;
toastStore.AddToast(message, "alert", 8000);
}; };
const GetCustomerTipList = async () => { const GetCustomerTipList = async () => {