Ukweli / web /src /components /ReportCard.vue
Benard John
Initial commit
42a0d15
Raw
History Blame Contribute Delete
4.28 kB
<template>
<div class="report-card" @click="$emit('click')">
<div class="card-header">
<n-tag :type="typeColor" size="small" class="type-tag">
{{ formatType(report.report_type) }}
</n-tag>
<n-tag v-if="report.fiscal_year" size="small" class="fy-tag">
FY {{ report.fiscal_year }}
</n-tag>
</div>
<h3 class="report-title">{{ report.title }}</h3>
<div class="report-meta">
<div v-if="report.auditee" class="meta-item">
<n-icon size="14"><business-outline /></n-icon>
<span>{{ report.auditee }}</span>
</div>
<div v-if="report.county_name" class="meta-item">
<n-icon size="14"><location-outline /></n-icon>
<span>{{ report.county_name }}</span>
</div>
<div v-if="report.page_count" class="meta-item">
<n-icon size="14"><document-text-outline /></n-icon>
<span>{{ report.page_count }} pages</span>
</div>
<div v-if="report.file_size_bytes" class="meta-item">
<n-icon size="14"><download-outline /></n-icon>
<span>{{ formatSize(report.file_size_bytes) }}</span>
</div>
</div>
<div v-if="report.audit_opinion" class="opinion-badge">
<n-tag :type="opinionColor" size="small">
{{ report.audit_opinion }} opinion
</n-tag>
</div>
<div class="card-footer">
<span class="date">{{ formatDate(report.crawled_at) }}</span>
<n-button text type="primary" size="small" class="view-btn">
View Report
<template #icon><n-icon><chevron-forward-outline /></n-icon></template>
</n-button>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import type { Report } from '@/types'
import { format } from 'date-fns'
import {
BusinessOutline,
LocationOutline,
DocumentTextOutline,
DownloadOutline,
ChevronForwardOutline,
} from '@vicons/ionicons5'
const props = defineProps<{
report: Report
}>()
defineEmits<{
click: []
}>()
const typeColor = computed(() => {
const colors: Record<string, any> = {
financial_audit: 'default',
county_audit: 'success',
special_audit: 'warning',
performance_audit: 'info',
summary_report: 'primary',
public_debt_audit: 'error',
}
return colors[props.report.report_type] || 'default'
})
const opinionColor = computed(() => {
const colors: Record<string, any> = {
unqualified: 'success',
qualified: 'warning',
adverse: 'error',
disclaimer: 'error',
}
return colors[props.report.audit_opinion || ''] || 'default'
})
const formatType = (type: string) => {
return type.replace(/_/g, ' ').replace(/\w/g, (l: string) => l.toUpperCase())
}
const formatSize = (bytes: number) => {
if (bytes < 1024) return bytes + ' B'
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'
return (bytes / (1024 * 1024)).toFixed(1) + ' MB'
}
const formatDate = (date: string) => {
return format(new Date(date), 'MMM d, yyyy')
}
</script>
<style scoped lang="scss">
.report-card {
background: var(--bg-primary);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 20px;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
border-color: var(--primary);
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
}
.card-header {
display: flex;
gap: 8px;
margin-bottom: 12px;
flex-wrap: wrap;
}
.type-tag {
font-weight: 500;
}
.fy-tag {
background: var(--bg-tertiary);
}
.report-title {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
line-height: 1.4;
margin-bottom: 12px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.report-meta {
display: flex;
flex-direction: column;
gap: 6px;
margin-bottom: 12px;
}
.meta-item {
display: flex;
align-items: center;
gap: 6px;
font-size: 13px;
color: var(--text-secondary);
.n-icon {
color: var(--text-muted);
}
}
.opinion-badge {
margin-bottom: 12px;
}
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 12px;
border-top: 1px solid var(--border);
}
.date {
font-size: 12px;
color: var(--text-muted);
}
.view-btn {
font-size: 13px;
}
</style>