Add summary section to index.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="['bg-white shadow rounded-lg p-4 lg:p-8 max-w-none', prose ? 'prose lg:prose-base prose-headings:font-light prose-headings:text-violet-900 prose-h1:mb-0' : '']">
|
||||
<div :class="['bg-white shadow rounded-lg p-4 lg:p-8 max-w-none', prose ? 'prose lg:prose-base prose-headings:font-light prose-headings:text-violet-900 prose-h1:mb-0 prose-a:text-violet-700 prose-a:font-normal' : '']">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
<template>
|
||||
<StatCard>
|
||||
<template v-slot:heading>Confirmed Cases {{last_report_date ? humanDate(last_report_date) : ''}}</template>
|
||||
<template v-slot:value v-if="chips.today_cases != null">
|
||||
{{chips.today_cases.toLocaleString()}}
|
||||
<template v-slot:value v-if="chips.current_cases != null">
|
||||
{{chips.current_cases.toLocaleString()}}
|
||||
</template>
|
||||
<template v-slot:meta v-if="chips.today_case_change != null">
|
||||
<span v-if="chips.today_case_change > 0">Up </span>
|
||||
<span v-if="chips.today_case_change < 0">Down </span>
|
||||
<span :class="{'font-semibold': true, 'text-red-600': chips.today_case_change > 0, 'text-green-600': chips.today_case_change < 0}">
|
||||
{{Math.abs(chips.today_case_change).toLocaleString()}} ({{chips.today_case_change_percent}}%)
|
||||
<template v-slot:meta v-if="chips.current_case_change != null">
|
||||
<span v-if="chips.current_case_change > 0">Up </span>
|
||||
<span v-if="chips.current_case_change < 0">Down </span>
|
||||
<span :class="{'font-semibold': true, 'text-red-600': chips.current_case_change > 0, 'text-green-600': chips.current_case_change < 0}">
|
||||
{{Math.abs(chips.current_case_change).toLocaleString()}} ({{chips.current_case_change_percent}}%)
|
||||
</span> compared to previous day
|
||||
</template>
|
||||
</StatCard>
|
||||
|
||||
<StatCard>
|
||||
<template v-slot:heading>Cases per 10,000 Residents {{last_report_date ? humanDate(last_report_date) : ''}}</template>
|
||||
<template v-slot:value v-if="chips.today_cases_per_capita != null">
|
||||
<span v-if="chips.population">~{{chips.today_cases_per_capita.toLocaleString()}}</span>
|
||||
<template v-slot:value v-if="chips.current_cases_per_capita != null">
|
||||
<span v-if="chips.population">~{{chips.current_cases_per_capita.toLocaleString()}}</span>
|
||||
<span v-else>No Data</span>
|
||||
</template>
|
||||
<template v-slot:meta v-if="chips.population">
|
||||
<span class="text-violet-500 font-semibold">{{chips.today_cases.toLocaleString()}}</span> out of <span class="text-violet-500 font-semibold">{{chips.population.toLocaleString()}}</span> residents
|
||||
<span class="text-violet-500 font-semibold">{{chips.current_cases.toLocaleString()}}</span> out of <span class="text-violet-500 font-semibold">{{chips.population.toLocaleString()}}</span> residents
|
||||
</template>
|
||||
</StatCard>
|
||||
|
||||
@@ -68,14 +68,14 @@ const chips = reactive({
|
||||
return col(data, r, 'population')
|
||||
}),
|
||||
|
||||
today_cases: computed(() => {
|
||||
current_cases: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
const r = data.value.rows.at(-1)
|
||||
return col(data, r, 'cases')
|
||||
}),
|
||||
|
||||
today_case_change: computed(() => {
|
||||
current_case_change: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
const today = col(data, data.value.rows.at(-1), 'cases')
|
||||
@@ -83,7 +83,7 @@ const chips = reactive({
|
||||
return today - yesterday
|
||||
}),
|
||||
|
||||
today_case_change_percent: computed(() => {
|
||||
current_case_change_percent: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
const today = col(data, data.value.rows.at(-1), 'cases')
|
||||
@@ -100,7 +100,7 @@ const chips = reactive({
|
||||
return Math.abs(percent.toFixed(1))
|
||||
}),
|
||||
|
||||
today_cases_per_capita: computed(() => {
|
||||
current_cases_per_capita: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
const r = data.value.rows.at(-1)
|
||||
|
||||
@@ -28,8 +28,6 @@ watch(() => store.parameters.value.county, () => {
|
||||
refreshData()
|
||||
})
|
||||
|
||||
if (globalThis.window) {
|
||||
refreshData()
|
||||
}
|
||||
if (globalThis.window) refreshData()
|
||||
|
||||
export default store
|
||||
|
||||
@@ -8,8 +8,6 @@ async function refreshData() {
|
||||
store.data.value = await fetch(`/data/overall/cases/combined.json`).then(res => res.json())
|
||||
}
|
||||
|
||||
if (globalThis.window) {
|
||||
refreshData()
|
||||
}
|
||||
if (globalThis.window) refreshData()
|
||||
|
||||
export default store
|
||||
|
||||
@@ -28,8 +28,6 @@ watch(() => store.parameters.value.county, () => {
|
||||
refreshData()
|
||||
})
|
||||
|
||||
if (globalThis.window) {
|
||||
refreshData()
|
||||
}
|
||||
if (globalThis.window) refreshData()
|
||||
|
||||
export default store
|
||||
|
||||
@@ -8,8 +8,6 @@ async function refreshData() {
|
||||
store.data.value = await fetch(`/data/overall/deaths/combined.json`).then(res => res.json())
|
||||
}
|
||||
|
||||
if (globalThis.window) {
|
||||
refreshData()
|
||||
}
|
||||
if (globalThis.window) refreshData()
|
||||
|
||||
export default store
|
||||
|
||||
@@ -28,8 +28,6 @@ watch(() => store.parameters.value.county, () => {
|
||||
refreshData()
|
||||
})
|
||||
|
||||
if (globalThis.window) {
|
||||
refreshData()
|
||||
}
|
||||
if (globalThis.window) refreshData()
|
||||
|
||||
export default store
|
||||
|
||||
@@ -5,11 +5,9 @@ const store = {
|
||||
}
|
||||
|
||||
async function refreshData() {
|
||||
store.data.value = await fetch(`/data/state/hospitalizations/combined.json`).then(res => res.json())
|
||||
store.data.value = await fetch(`/data/overall/hospitalizations/combined.json`).then(res => res.json())
|
||||
}
|
||||
|
||||
if (globalThis.window) {
|
||||
refreshData()
|
||||
}
|
||||
if (globalThis.window) refreshData()
|
||||
|
||||
export default store
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
</StatCard>
|
||||
|
||||
<StatCard>
|
||||
<template v-slot:heading>7-Day Percent Positive</template>
|
||||
<template v-slot:heading>7-Day Test Positivity</template>
|
||||
<template v-slot:value v-if="chips.percent_positive">
|
||||
<span :class="{
|
||||
'text-green-600': chips.percent_positive < 2,
|
||||
@@ -41,7 +41,7 @@
|
||||
{{chips.percent_positive}}<span class="font-light">%</span>
|
||||
</span>
|
||||
</template>
|
||||
<template v-slot:meta v-if="chips.positive">
|
||||
<template v-slot:meta v-if="chips.percent_positive">
|
||||
<div class="inline-flex flex-wrap text-xs -mx-1">
|
||||
<div class="flex-1">
|
||||
<div class="px-1 text-green-600 font-medium whitespace-nowrap">Low: < 2%</div>
|
||||
|
||||
@@ -22,8 +22,6 @@ watch(() => store.parameters.value.county, () => {
|
||||
refreshData()
|
||||
})
|
||||
|
||||
if (globalThis.window) {
|
||||
refreshData()
|
||||
}
|
||||
if (globalThis.window) refreshData()
|
||||
|
||||
export default store
|
||||
|
||||
@@ -23,8 +23,6 @@ async function refreshData() {
|
||||
}
|
||||
}
|
||||
|
||||
if (globalThis.window) {
|
||||
refreshData()
|
||||
}
|
||||
if (globalThis.window) refreshData()
|
||||
|
||||
export default store
|
||||
|
||||
@@ -8,8 +8,6 @@ async function refreshData() {
|
||||
store.data.value = await fetch(`/data/risk/health-conditions/health-conditions.json`).then(res => res.json())
|
||||
}
|
||||
|
||||
if (globalThis.window) {
|
||||
refreshData()
|
||||
}
|
||||
if (globalThis.window) refreshData()
|
||||
|
||||
export default store
|
||||
|
||||
216
src/components/charts/summary/ChipsSummary.vue
Normal file
216
src/components/charts/summary/ChipsSummary.vue
Normal file
@@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<StatCard>
|
||||
<template v-slot:heading>Tests Reported {{last_report_date ? humanDate(last_report_date) : ''}}</template>
|
||||
<template v-slot:value v-if="chips.current_testing">
|
||||
{{chips.current_testing.toLocaleString()}}
|
||||
</template>
|
||||
<template v-slot:meta v-if="chips.current_testing">
|
||||
PCR + Antigen
|
||||
</template>
|
||||
<template v-slot:meta v-if="chips.current_testing_change != null">
|
||||
<span v-if="chips.current_testing_change > 0">Up </span>
|
||||
<span v-if="chips.current_testing_change < 0">Down </span>
|
||||
<span class="font-semibold text-violet-600">
|
||||
{{Math.abs(chips.current_testing_change).toLocaleString()}} ({{chips.current_testing_change_percent}}%)
|
||||
</span> compared to previous day
|
||||
</template>
|
||||
</StatCard>
|
||||
|
||||
<StatCard>
|
||||
<template v-slot:heading>7-Day Test Positivity</template>
|
||||
<template v-slot:value v-if="chips.percent_positive">
|
||||
<span :class="{
|
||||
'text-green-600': chips.percent_positive < 2,
|
||||
'text-yellow-500': chips.percent_positive >= 2 && chips.percent_positive < 5,
|
||||
'text-orange-500': chips.percent_positive >= 5 && chips.percent_positive < 20,
|
||||
'text-red-800': chips.percent_positive > 20
|
||||
}">
|
||||
{{chips.percent_positive}}<span class="font-light">%</span>
|
||||
</span>
|
||||
</template>
|
||||
<template v-slot:meta v-if="chips.percent_positive">
|
||||
<div class="inline-flex flex-wrap text-xs -mx-1">
|
||||
<div class="flex-1">
|
||||
<div class="px-1 text-green-600 font-medium whitespace-nowrap">Low: < 2%</div>
|
||||
<div class="px-1 text-yellow-600 font-medium whitespace-nowrap">Moderate < 5%</div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="px-1 text-orange-600 font-medium whitespace-nowrap">High < 20%</div>
|
||||
<div class="px-1 text-red-700 font-medium whitespace-nowrap">Very high > 20%</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="link block mt-2 text-xs" href="https://www.who.int/publications/i/item/considerations-in-adjusting-public-health-and-social-measures-in-the-context-of-covid-19-interim-guidanceLow">
|
||||
Source, see p. 18
|
||||
</a>
|
||||
</template>
|
||||
</StatCard>
|
||||
|
||||
<StatCard>
|
||||
<template v-slot:heading>Confirmed Cases {{last_report_date ? humanDate(last_report_date) : ''}}</template>
|
||||
<template v-slot:value v-if="chips.current_cases != null">
|
||||
{{chips.current_cases.toLocaleString()}}
|
||||
</template>
|
||||
<template v-slot:meta v-if="chips.current_case_change != null">
|
||||
<span v-if="chips.current_case_change > 0">Up </span>
|
||||
<span v-if="chips.current_case_change < 0">Down </span>
|
||||
<span :class="{'font-semibold': true, 'text-red-600': chips.current_case_change > 0, 'text-green-600': chips.current_case_change < 0}">
|
||||
{{Math.abs(chips.current_case_change).toLocaleString()}} ({{chips.current_case_change_percent}}%)
|
||||
</span> compared to previous day
|
||||
</template>
|
||||
</StatCard>
|
||||
|
||||
<StatCard>
|
||||
<template v-slot:heading>Confirmed Deaths {{last_report_date ? humanDate(last_report_date) : ''}}</template>
|
||||
<template v-slot:value v-if="chips.current_deaths != null">
|
||||
{{chips.current_deaths.toLocaleString()}}
|
||||
</template>
|
||||
<template v-slot:meta v-if="chips.current_death_change != null">
|
||||
<span v-if="chips.current_death_change > 0">Up </span>
|
||||
<span v-if="chips.current_death_change < 0">Down </span>
|
||||
<span :class="{'font-semibold': true, 'text-red-600': chips.current_death_change > 0, 'text-green-600': chips.current_death_change < 0}">
|
||||
{{Math.abs(chips.current_death_change).toLocaleString()}} ({{chips.current_death_change_percent}}%)
|
||||
</span> compared to previous day
|
||||
</template>
|
||||
</StatCard>
|
||||
|
||||
<StatCard>
|
||||
<template v-slot:heading>Total Confirmed Cases</template>
|
||||
<template v-slot:value v-if="chips.total_cases != null">
|
||||
{{chips.total_cases.toLocaleString()}}
|
||||
</template>
|
||||
</StatCard>
|
||||
|
||||
<StatCard>
|
||||
<template v-slot:heading>Total Confirmed Deaths</template>
|
||||
<template v-slot:value v-if="chips.total_deaths != null">
|
||||
{{chips.total_deaths.toLocaleString()}}
|
||||
</template>
|
||||
</StatCard>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, computed } from 'vue'
|
||||
import { col, humanDate } from '@/components/charts/util'
|
||||
import store from '@/components/charts/summary/store.js'
|
||||
|
||||
const data = store.data
|
||||
|
||||
const last_report_date = computed(() => {
|
||||
if (!data.value) return null
|
||||
return data.value.last_report_date
|
||||
})
|
||||
|
||||
const chips = reactive({
|
||||
current_testing: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
return col(data.value.testing, data.value.testing.current, 'combined_performed')
|
||||
}),
|
||||
|
||||
current_testing_change: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
const today = col(data.value.testing, data.value.testing.current, 'combined_performed')
|
||||
const yesterday = col(data.value.testing, data.value.testing.prev, 'combined_performed')
|
||||
return today - yesterday
|
||||
}),
|
||||
|
||||
current_testing_change_percent: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
const today = col(data.value.testing, data.value.testing.current, 'combined_performed')
|
||||
const yesterday = col(data.value.testing, data.value.testing.prev, 'combined_performed')
|
||||
const change = today - yesterday
|
||||
|
||||
const percent = change > 0
|
||||
? ((change / yesterday) * 100)
|
||||
: ((change / today) * 100)
|
||||
|
||||
if (Math.abs(percent) === Infinity) return 100
|
||||
if (isNaN(percent)) return 0
|
||||
|
||||
return Math.abs(percent.toFixed(1))
|
||||
}),
|
||||
|
||||
percent_positive: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
return col(data.value.testing, data.value.testing.current, 'seven_day_percent_positive')
|
||||
}),
|
||||
|
||||
current_cases: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
return col(data.value.cases, data.value.cases.current, 'cases')
|
||||
}),
|
||||
|
||||
current_case_change: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
const today = col(data.value.cases, data.value.cases.current, 'cases')
|
||||
const yesterday = col(data.value.cases, data.value.cases.prev, 'cases')
|
||||
return today - yesterday
|
||||
}),
|
||||
|
||||
current_case_change_percent: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
const today = col(data.value.cases, data.value.cases.current, 'cases')
|
||||
const yesterday = col(data.value.cases, data.value.cases.prev, 'cases')
|
||||
const change = today - yesterday
|
||||
|
||||
const percent = change > 0
|
||||
? ((change / yesterday) * 100)
|
||||
: ((change / today) * 100)
|
||||
|
||||
if (Math.abs(percent) === Infinity) return 100
|
||||
if (isNaN(percent)) return 0
|
||||
|
||||
return Math.abs(percent.toFixed(1))
|
||||
}),
|
||||
|
||||
current_deaths: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
return col(data.value.deaths, data.value.deaths.current, 'deaths')
|
||||
}),
|
||||
|
||||
current_death_change: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
const today = col(data.value.deaths, data.value.deaths.current, 'deaths')
|
||||
const yesterday = col(data.value.deaths, data.value.deaths.prev, 'deaths')
|
||||
return today - yesterday
|
||||
}),
|
||||
|
||||
current_death_change_percent: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
const today = col(data.value.deaths, data.value.deaths.current, 'deaths')
|
||||
const yesterday = col(data.value.deaths, data.value.deaths.prev, 'deaths')
|
||||
const change = today - yesterday
|
||||
|
||||
const percent = change > 0
|
||||
? ((change / yesterday) * 100)
|
||||
: ((change / today) * 100)
|
||||
|
||||
if (Math.abs(percent) === Infinity) return 100
|
||||
if (isNaN(percent)) return 0
|
||||
|
||||
return Math.abs(percent.toFixed(1))
|
||||
}),
|
||||
|
||||
total_cases: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
return col(data.value.cases, data.value.cases.current, 'total_cases')
|
||||
}),
|
||||
|
||||
total_deaths: computed(() => {
|
||||
if (!data.value) return null
|
||||
|
||||
return col(data.value.deaths, data.value.deaths.current, 'total_deaths')
|
||||
})
|
||||
})
|
||||
|
||||
</script>
|
||||
13
src/components/charts/summary/store.js
Normal file
13
src/components/charts/summary/store.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
|
||||
const store = {
|
||||
data: ref(null)
|
||||
}
|
||||
|
||||
async function refreshData() {
|
||||
store.data.value = await fetch(`/data/summary.json`).then(res => res.json())
|
||||
}
|
||||
|
||||
if (globalThis.window) refreshData()
|
||||
|
||||
export default store
|
||||
@@ -1,7 +1,7 @@
|
||||
export const colors = ['#f5f3ff','#ede9fe','#ddd6fe','#c4b5fd','#a78bfa','#8b5cf6','#7c3aed','#6d28d9','#5b21b6','#4c1d95']
|
||||
|
||||
export function col(data, row, column) {
|
||||
const index = data.value.headers.indexOf(column)
|
||||
const index = data.headers ? data.headers.indexOf(column) : data.value.headers.indexOf(column)
|
||||
if (index === -1) return null
|
||||
return row[index]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user