修复客户订单列设置持久化并统一最新计费量展示
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
import ts from 'typescript'
|
||||
import { computed, effectScope, ref, watch } from 'vue'
|
||||
import { readOrderColumnPreferences, saveOrderColumnPreferences } from './orderColumnPreferences.ts'
|
||||
|
||||
const page = readFileSync(new URL('../views/FirstLegOrdersView.vue', import.meta.url), 'utf8')
|
||||
const columnSource = page.slice(page.indexOf('const fixedOrderColumns:'), page.indexOf('const tablePage ='))
|
||||
const columnScript = ts.transpileModule(columnSource, { compilerOptions: { target: ts.ScriptTarget.ES2022 } }).outputText
|
||||
const createColumns = new Function('auth', 'ref', 'computed', 'watch', 'readOrderColumnPreferences', 'saveOrderColumnPreferences', `${columnScript}
|
||||
return { visibleColumnKeys, orderedColumnKeys, visibleOrderColumns, draggedColumnKey, moveOrderColumn, dropOrderColumn, resetOrderColumns }
|
||||
`)
|
||||
|
||||
function mockStorage(t) {
|
||||
const original = Object.getOwnPropertyDescriptor(globalThis, 'localStorage')
|
||||
const entries = new Map()
|
||||
const storage = {
|
||||
getItem: key => entries.get(key) ?? null,
|
||||
setItem: (key, value) => entries.set(key, value),
|
||||
}
|
||||
Object.defineProperty(globalThis, 'localStorage', { configurable: true, value: storage })
|
||||
t.after(() => {
|
||||
if (original) Object.defineProperty(globalThis, 'localStorage', original)
|
||||
else delete globalThis.localStorage
|
||||
})
|
||||
return storage
|
||||
}
|
||||
|
||||
function openColumns(t, ownerId = 'customer-one') {
|
||||
const scope = effectScope()
|
||||
const columns = scope.run(() => createColumns({ user: { id: ownerId } }, ref, computed, watch, readOrderColumnPreferences, saveOrderColumnPreferences))
|
||||
t.after(() => scope.stop())
|
||||
return { ...columns, close: () => scope.stop() }
|
||||
}
|
||||
|
||||
test('restores checked fields and both arrow and drag ordering after a fresh page setup', t => {
|
||||
mockStorage(t)
|
||||
const columns = openColumns(t)
|
||||
columns.visibleColumnKeys.value.push('destinationWarehouse', 'grossWeight', 'volume')
|
||||
columns.visibleColumnKeys.value.splice(columns.visibleColumnKeys.value.indexOf('createdAt'), 1)
|
||||
columns.moveOrderColumn('destinationWarehouse', -1)
|
||||
columns.draggedColumnKey.value = 'grossWeight'
|
||||
columns.dropOrderColumn('status')
|
||||
const visibleKeys = [...columns.visibleColumnKeys.value]
|
||||
const orderedKeys = [...columns.orderedColumnKeys.value]
|
||||
const renderedKeys = columns.visibleOrderColumns.value.map(column => column.key)
|
||||
columns.close()
|
||||
|
||||
const refreshed = openColumns(t)
|
||||
assert.deepEqual(refreshed.visibleColumnKeys.value, visibleKeys)
|
||||
assert.deepEqual(refreshed.orderedColumnKeys.value, orderedKeys)
|
||||
assert.deepEqual(refreshed.visibleOrderColumns.value.map(column => column.key), renderedKeys)
|
||||
assert.deepEqual(renderedKeys.slice(0, 3), ['selection', 'actions', 'inboundCode'])
|
||||
assert.equal(renderedKeys[3], 'grossWeight')
|
||||
})
|
||||
|
||||
test('persists restore-default and intentionally hiding every selectable field', t => {
|
||||
mockStorage(t)
|
||||
const columns = openColumns(t)
|
||||
const defaults = [...columns.visibleColumnKeys.value]
|
||||
const defaultOrder = [...columns.orderedColumnKeys.value]
|
||||
columns.visibleColumnKeys.value = []
|
||||
columns.close()
|
||||
const refreshed = openColumns(t)
|
||||
assert.deepEqual(refreshed.visibleColumnKeys.value, [])
|
||||
assert.deepEqual(refreshed.visibleOrderColumns.value.map(column => column.key), ['selection', 'actions', 'inboundCode'])
|
||||
|
||||
refreshed.moveOrderColumn('grossWeight', -1)
|
||||
refreshed.resetOrderColumns()
|
||||
refreshed.close()
|
||||
const reset = openColumns(t)
|
||||
assert.deepEqual(reset.visibleColumnKeys.value, defaults)
|
||||
assert.deepEqual(reset.orderedColumnKeys.value, defaultOrder)
|
||||
})
|
||||
|
||||
test('keeps the field selection separate for each signed-in account', t => {
|
||||
mockStorage(t)
|
||||
const first = openColumns(t)
|
||||
first.visibleColumnKeys.value = ['volume']
|
||||
first.close()
|
||||
const second = openColumns(t, 'customer-two')
|
||||
assert.notDeepEqual(second.visibleColumnKeys.value, ['volume'])
|
||||
second.visibleColumnKeys.value = ['grossWeight']
|
||||
second.close()
|
||||
assert.deepEqual(openColumns(t).visibleColumnKeys.value, ['volume'])
|
||||
assert.deepEqual(openColumns(t, 'customer-two').visibleColumnKeys.value, ['grossWeight'])
|
||||
})
|
||||
|
||||
test('discards unknown, duplicate and invalid keys and includes newly available fields in ordering', t => {
|
||||
mockStorage(t)
|
||||
saveOrderColumnPreferences('customer-one', {
|
||||
visibleColumnKeys: ['volume', 'removed', 'volume', null, 3],
|
||||
orderedColumnKeys: ['volume', 'removed', 'volume', false],
|
||||
})
|
||||
assert.deepEqual(readOrderColumnPreferences('customer-one', ['status', 'volume', 'newField'], ['status']), {
|
||||
visibleColumnKeys: ['volume'],
|
||||
orderedColumnKeys: ['volume', 'status', 'newField'],
|
||||
})
|
||||
})
|
||||
|
||||
test('uses the original defaults for missing or damaged saved preferences', t => {
|
||||
const storage = mockStorage(t)
|
||||
const defaults = { visibleColumnKeys: ['status'], orderedColumnKeys: ['status', 'volume'] }
|
||||
assert.deepEqual(readOrderColumnPreferences('customer-one', ['status', 'volume'], ['status']), defaults)
|
||||
saveOrderColumnPreferences('customer-one', defaults)
|
||||
const realGetItem = storage.getItem
|
||||
for (const saved of ['{invalid', 'null', '[]', '42', '{"visibleColumnKeys":"status","orderedColumnKeys":[]}']) {
|
||||
storage.getItem = () => saved
|
||||
assert.deepEqual(readOrderColumnPreferences('customer-one', ['status', 'volume'], ['status']), defaults)
|
||||
}
|
||||
storage.getItem = realGetItem
|
||||
})
|
||||
|
||||
test('keeps column controls usable when browser storage is blocked', t => {
|
||||
const storage = mockStorage(t)
|
||||
storage.getItem = () => { throw new Error('Storage denied') }
|
||||
storage.setItem = () => { throw new Error('Storage denied') }
|
||||
const columns = openColumns(t)
|
||||
assert.ok(columns.visibleColumnKeys.value.includes('status'))
|
||||
assert.doesNotThrow(() => { columns.visibleColumnKeys.value.push('volume') })
|
||||
assert.doesNotThrow(() => columns.resetOrderColumns())
|
||||
})
|
||||
|
||||
test('does not read or save preferences without an account', t => {
|
||||
const storage = mockStorage(t)
|
||||
let storageCalls = 0
|
||||
storage.getItem = () => { storageCalls++; return null }
|
||||
storage.setItem = () => { storageCalls++ }
|
||||
const columns = openColumns(t, '')
|
||||
columns.visibleColumnKeys.value = ['volume']
|
||||
assert.equal(openColumns(t, '').visibleColumnKeys.value.includes('status'), true)
|
||||
assert.equal(storageCalls, 0)
|
||||
})
|
||||
@@ -0,0 +1,35 @@
|
||||
interface OrderColumnPreferences {
|
||||
visibleColumnKeys: string[]
|
||||
orderedColumnKeys: string[]
|
||||
}
|
||||
|
||||
function storageKey(ownerId: string) {
|
||||
return `cc:client:first-leg:order-columns:${ownerId}`
|
||||
}
|
||||
|
||||
export function readOrderColumnPreferences(ownerId: string, selectableKeys: string[], defaultVisibleKeys: string[]): OrderColumnPreferences {
|
||||
const defaults = { visibleColumnKeys: [...defaultVisibleKeys], orderedColumnKeys: [...selectableKeys] }
|
||||
if (!ownerId) return defaults
|
||||
try {
|
||||
const saved: unknown = JSON.parse(localStorage.getItem(storageKey(ownerId)) ?? 'null')
|
||||
if (!saved || typeof saved !== 'object') return defaults
|
||||
const preferences = saved as Partial<OrderColumnPreferences>
|
||||
if (!Array.isArray(preferences.visibleColumnKeys) || !Array.isArray(preferences.orderedColumnKeys)) return defaults
|
||||
const validKeys = (keys: unknown[]) => [...new Set(keys.filter((key): key is string => typeof key === 'string' && selectableKeys.includes(key)))]
|
||||
return {
|
||||
visibleColumnKeys: validKeys(preferences.visibleColumnKeys),
|
||||
orderedColumnKeys: [...new Set([...validKeys(preferences.orderedColumnKeys), ...selectableKeys])],
|
||||
}
|
||||
} catch {
|
||||
return defaults
|
||||
}
|
||||
}
|
||||
|
||||
export function saveOrderColumnPreferences(ownerId: string, preferences: OrderColumnPreferences) {
|
||||
if (!ownerId) return
|
||||
try {
|
||||
localStorage.setItem(storageKey(ownerId), JSON.stringify(preferences))
|
||||
} catch {
|
||||
// Column selection remains usable when browser storage is unavailable.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user