| <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> |
| |