Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-06-04 18:23:23 +03:00
parent 5b8880693e
commit e1e9d4baf1

View File

@@ -292,7 +292,7 @@
<script setup> <script setup>
import { computed, onActivated, onBeforeUnmount, onMounted, ref } from 'vue' import { computed, onActivated, onBeforeUnmount, onMounted, ref } from 'vue'
import { Notify } from 'quasar' import { Dialog, Notify } from 'quasar'
import api, { download } from 'src/services/api' import api, { download } from 'src/services/api'
import { usePermissionStore } from 'stores/permissionStore' import { usePermissionStore } from 'stores/permissionStore'
@@ -539,6 +539,7 @@ const selectedDirtyCount = computed(() => rows.value.reduce((count, row) => coun
const selectedCount = computed(() => Object.keys(selectedKeyMap.value || {}).length) const selectedCount = computed(() => Object.keys(selectedKeyMap.value || {}).length)
const copySelectedCount = computed(() => copySelectedKeys.value.length) const copySelectedCount = computed(() => copySelectedKeys.value.length)
const canCopySelected = computed(() => copySelectedCount.value >= 2) const canCopySelected = computed(() => copySelectedCount.value >= 2)
const hasUnsavedLocalChanges = computed(() => rows.value.some(row => Boolean(row?._dirty)))
const hasAnyFilter = computed(() => { const hasAnyFilter = computed(() => {
return [...headerFilterFieldSet].some(field => hasFilter(field)) return [...headerFilterFieldSet].some(field => hasFilter(field))
}) })
@@ -878,7 +879,10 @@ async function onImportFileChange (event) {
return return
} }
const importItems = [] const rowMap = new Map(rows.value.map(row => [buildImportRowKeyFromObject(row), row]))
let matched = 0
let updated = 0
let skipped = 0
for (let i = 1; i < matrix.length; i++) { for (let i = 1; i < matrix.length; i++) {
const csvRow = matrix[i] const csvRow = matrix[i]
@@ -887,10 +891,14 @@ async function onImportFileChange (event) {
identity[field] = String(csvRow[keyHeaderIndexes[label]] ?? '').trim().replace(/^'/, '').replace(/^=\s*"([\s\S]*)"$/, '$1') identity[field] = String(csvRow[keyHeaderIndexes[label]] ?? '').trim().replace(/^'/, '').replace(/^=\s*"([\s\S]*)"$/, '$1')
} }
const item = { const target = rowMap.get(buildImportRowKeyFromObject(identity))
...identity, if (!target) {
is_active: true skipped++
continue
} }
matched++
let rowChanged = false
for (const [headerLabel, field] of Object.entries(importFieldMap)) { for (const [headerLabel, field] of Object.entries(importFieldMap)) {
const idx = headers.indexOf(headerLabel) const idx = headers.indexOf(headerLabel)
if (idx < 0) continue if (idx < 0) continue
@@ -898,33 +906,34 @@ async function onImportFileChange (event) {
if (field === 'is_active') { if (field === 'is_active') {
const next = parseImportedBoolean(rawValue) const next = parseImportedBoolean(rawValue)
if (next !== null) item.is_active = next if (next !== null && Boolean(target.is_active) !== next) {
target.is_active = next
rowChanged = true
}
continue continue
} }
item[field] = parseImportedNumber(rawValue) const next = parseImportedNumber(rawValue)
if (Number(target[field] ?? 0) !== next) {
target[field] = next
rowChanged = true
} }
importItems.push(item)
} }
if (importItems.length === 0) { if (rowChanged) {
Notify.create({ type: 'warning', message: 'CSV icinde islenecek satir bulunamadi' }) markDirty(target)
updated++
}
}
if (matched === 0) {
Notify.create({ type: 'warning', message: 'CSV icindeki satirlar ekrandaki kayitlarla eslesmedi' })
return return
} }
const response = await api.request({
method: 'POST',
url: '/pricing/pricing-rules/import',
data: { items: importItems },
timeout: 180000
})
await loadRows()
const stats = response?.data || {}
Notify.create({ Notify.create({
type: 'positive', type: 'positive',
message: `CSV yuklendi. Islenen: ${stats.processed ?? importItems.length}, eslesen: ${stats.matched ?? stats.updated ?? importItems.length}, kaydedilen: ${stats.updated ?? importItems.length}, atlanan: ${stats.skipped ?? 0}, hata: ${stats.error_count ?? 0}` message: `CSV yuklendi. Islenen: ${matrix.length - 1}, eslesen: ${matched}, guncellenen: ${updated}, atlanan: ${skipped}`
}) })
} catch (err) { } catch (err) {
Notify.create({ type: 'negative', message: err?.message || 'CSV okunamadi' }) Notify.create({ type: 'negative', message: err?.message || 'CSV okunamadi' })
@@ -1031,6 +1040,21 @@ function clearAllFilters () {
} }
async function refreshRows () { async function refreshRows () {
if (hasUnsavedLocalChanges.value) {
const confirmed = await new Promise(resolve => {
Dialog.create({
title: 'Degisiklikler silinecek',
message: 'Kaydedilmemis tum degisiklikler silinecek. Devam etmek istiyor musunuz?',
cancel: true,
persistent: true
})
.onOk(() => resolve(true))
.onCancel(() => resolve(false))
.onDismiss(() => resolve(false))
})
if (!confirmed) return
}
clearAllFilters() clearAllFilters()
selectedKeyMap.value = {} selectedKeyMap.value = {}
copySelectedKeys.value = [] copySelectedKeys.value = []