big refactor

This commit is contained in:
2026-04-29 16:25:11 -05:00
parent 6c411ce2b6
commit 8265fb689a
156 changed files with 15845 additions and 50373 deletions

View File

@@ -0,0 +1,118 @@
<script setup lang="ts">
defineOptions({ name: 'NestedJsonViewer' })
interface Props {
data: any
depth?: number
}
const props = defineProps<Props>()
const depth = computed(() => props.depth || 0)
// Font size decreases with depth: 12px base, -1px per level
const fontSize = computed(() => Math.max(10, 12 - depth.value * 1))
// Padding decreases with depth
const padding = computed(() => Math.max(1, 4 - depth.value))
function isObject(value: any): boolean {
return value !== null && typeof value === 'object' && !Array.isArray(value)
}
function isArray(value: any): boolean {
return Array.isArray(value)
}
function isDateString(value: string): boolean {
if (typeof value !== 'string') return false
return /^\d{4}-\d{2}-\d{2}/.test(value) && !isNaN(Date.parse(value))
}
function formatValue(value: any): string {
if (value === null || value === undefined) return '—'
if (typeof value === 'boolean') return value ? 'Yes' : 'No'
if (typeof value === 'number') return value.toLocaleString()
if (isDateString(value)) {
try {
return new Date(value).toLocaleDateString('es-PA', {
day: '2-digit',
month: 'short',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
})
} catch {
return value
}
}
return String(value)
}
function formatKey(key: string): string {
return key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())
}
</script>
<template>
<div class="nested-json-viewer" :style="{ fontSize: fontSize + 'px' }">
<!-- Object: render as table -->
<table v-if="isObject(data) && Object.keys(data).length > 0" class="json-table border rounded overflow-hidden mb-2">
<tbody>
<tr v-for="(value, key) in data" :key="key" class="border-t border-gray-200 hover:bg-gray-50">
<td class="field-name px-3 py-2 font-medium text-gray-600 align-top whitespace-nowrap" :style="{ padding: padding + 'px', fontSize: (fontSize - 1) + 'px' }">
{{ formatKey(key) }}
</td>
<td class="field-value px-3 py-2 text-gray-900 align-top" :style="{ padding: padding + 'px' }">
<NestedJsonViewer :data="value" :depth="depth + 1" />
</td>
</tr>
</tbody>
</table>
<div v-else-if="isObject(data) && Object.keys(data).length === 0" class="text-gray-400 italic" :style="{ fontSize: fontSize + 'px' }">
Empty object
</div>
<!-- Array -->
<div v-else-if="isArray(data)" class="array-container">
<div v-for="(item, index) in data" :key="index" class="array-item mb-2 border rounded p-2">
<div class="text-xs font-semibold text-gray-500 mb-1">[{{ index }}]</div>
<NestedJsonViewer :data="item" :depth="depth + 1" />
</div>
<div v-if="data.length === 0" class="text-gray-400 italic" :style="{ fontSize: fontSize + 'px' }">
Empty array
</div>
</div>
<!-- Primitive -->
<span v-else class="primitive-value" :class="{ 'text-gray-400': data === '—' }">
{{ formatValue(data) }}
</span>
</div>
</template>
<style scoped>
.nested-json-viewer {
width: 100%;
}
.json-table {
width: 100%;
border-collapse: collapse;
background: white;
}
.field-name {
min-width: 120px;
background: #f9fafb;
}
.field-value {
word-break: break-word;
}
.array-item {
background: white;
border-color: var(--card-border, #e5e7eb);
}
</style>