mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-09-04 17:23:25 +00:00
FIX - align health app with observable data
Remove inferred condition, recovery, and player-health values from the Health UI and NUI contract. Keep browser mocks and tests aligned, correct the default development API port, remove the misleading Medical ID disclosure, and apply the shared page gutter to prevent clipped cards.
This commit is contained in:
@@ -22,7 +22,6 @@ const overview: HealthOverview = {
|
||||
playerName: 'Alex Morgan',
|
||||
},
|
||||
previousWeekSteps: 0,
|
||||
snapshot: { healthPercent: 100 },
|
||||
}
|
||||
|
||||
describe('health store', () => {
|
||||
|
||||
@@ -368,10 +368,6 @@ const healthFallbackLocales = {
|
||||
kilocalories: '{count} kcal',
|
||||
thisWeek: 'This week',
|
||||
snapshot: 'Health snapshot',
|
||||
condition: 'Condition',
|
||||
recovery: 'Recovery',
|
||||
currentHealth: 'Current health',
|
||||
conditions: { good: 'Good', fair: 'Fair', low: 'Low' },
|
||||
trends: {
|
||||
title: 'Trends',
|
||||
week: 'Week',
|
||||
@@ -402,8 +398,6 @@ const healthFallbackLocales = {
|
||||
phoneNumber: 'Phone number',
|
||||
emergencyCall: 'Emergency call',
|
||||
callContact: 'Call emergency contact',
|
||||
privacy:
|
||||
'Information is stored with your character and can be shown to emergency services.',
|
||||
saveFailed: 'Medical ID could not be saved.',
|
||||
},
|
||||
errors: {
|
||||
|
||||
@@ -17,17 +17,12 @@ export type HealthMedicalId = {
|
||||
playerName: string
|
||||
}
|
||||
|
||||
export type HealthSnapshot = {
|
||||
healthPercent: number
|
||||
}
|
||||
|
||||
export type HealthOverview = {
|
||||
dailyStepGoal: number
|
||||
days: HealthActivityDay[]
|
||||
emergencyNumber: string
|
||||
medicalId: HealthMedicalId
|
||||
previousWeekSteps: number
|
||||
snapshot: HealthSnapshot
|
||||
}
|
||||
|
||||
export type HealthMedicalIdInput = Omit<HealthMedicalId, 'playerName'>
|
||||
|
||||
@@ -34,7 +34,7 @@ describe('nuiCall', () => {
|
||||
})
|
||||
expect(vi.getTimerCount()).toBe(0)
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
'http://localhost:3001/api/test',
|
||||
'http://localhost:3002/api/test',
|
||||
expect.objectContaining({ signal: expect.any(AbortSignal) }),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -15,7 +15,7 @@ export async function nuiCall<T = unknown>(
|
||||
'apiPort',
|
||||
)
|
||||
const baseUrl = import.meta.env.DEV
|
||||
? `http://localhost:${developmentPort ?? '3001'}/api`
|
||||
? `http://localhost:${developmentPort ?? '3002'}/api`
|
||||
: `https://${resourceName}`
|
||||
const requestData = import.meta.env.DEV
|
||||
? {
|
||||
|
||||
@@ -8,6 +8,7 @@ describe('HealthApp Sky UI contract', () => {
|
||||
expect(source).toContain('<SkyAppPage')
|
||||
expect(source).toContain('<SkyNavbar')
|
||||
expect(source).toContain('<SkyScrollArea')
|
||||
expect(source).toContain('padded with-tabbar')
|
||||
expect(source).toContain('with-tabbar')
|
||||
expect(source).toContain('<SkyPillNavigation')
|
||||
expect(source).not.toContain(
|
||||
@@ -26,4 +27,10 @@ describe('HealthApp Sky UI contract', () => {
|
||||
expect(source).toContain("nuiCall('calls:dial'")
|
||||
expect(source).toContain('health.saveMedicalId')
|
||||
})
|
||||
|
||||
it('does not present values the phone cannot observe', () => {
|
||||
expect(source).not.toContain('healthPercent')
|
||||
expect(source).not.toContain('recoveryPercent')
|
||||
expect(source).not.toContain('conditionKey')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
MapPin,
|
||||
Phone,
|
||||
RefreshCw,
|
||||
ShieldCheck,
|
||||
Timer,
|
||||
UserRound,
|
||||
} from 'lucide-vue-next'
|
||||
@@ -102,20 +101,6 @@ const trendCopy = computed(() => {
|
||||
}
|
||||
return phone.t('Apps.health.trends.same')
|
||||
})
|
||||
const healthPercent = computed(
|
||||
() => overview.value?.snapshot.healthPercent ?? 0,
|
||||
)
|
||||
const conditionKey = computed(() =>
|
||||
healthPercent.value >= 70
|
||||
? 'good'
|
||||
: healthPercent.value >= 35
|
||||
? 'fair'
|
||||
: 'low',
|
||||
)
|
||||
const recoveryPercent = computed(() => {
|
||||
const activeLoad = Math.min(20, Math.round((today.value?.activeSeconds ?? 0) / 180))
|
||||
return Math.max(0, Math.min(100, healthPercent.value - activeLoad))
|
||||
})
|
||||
const dateRange = computed(() => {
|
||||
if (!visibleDays.value.length) return ''
|
||||
const start = new Date(`${visibleDays.value[0].date}T12:00:00`)
|
||||
@@ -248,7 +233,7 @@ onBeforeUnmount(() => {
|
||||
</template>
|
||||
</SkyNavbar>
|
||||
|
||||
<SkyScrollArea class="health-content" with-tabbar>
|
||||
<SkyScrollArea class="health-content" padded with-tabbar>
|
||||
<div v-if="health.isLoading && !overview" class="health-loading">
|
||||
<SkySpinner />
|
||||
<span>{{ phone.t('Apps.health.loading') }}</span>
|
||||
@@ -333,34 +318,6 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="health-section">
|
||||
<h2>{{ phone.t('Apps.health.snapshot') }}</h2>
|
||||
<div class="health-list">
|
||||
<div class="health-list__row">
|
||||
<span class="health-list__icon health-list__icon--green">
|
||||
<HeartPulse :size="21" />
|
||||
</span>
|
||||
<span>{{ phone.t('Apps.health.condition') }}</span>
|
||||
<strong :class="`health-value--${conditionKey}`">{{
|
||||
phone.t(`Apps.health.conditions.${conditionKey}`)
|
||||
}}</strong>
|
||||
</div>
|
||||
<div class="health-list__row">
|
||||
<span class="health-list__icon health-list__icon--orange">
|
||||
<Activity :size="21" />
|
||||
</span>
|
||||
<span>{{ phone.t('Apps.health.recovery') }}</span>
|
||||
<strong>{{ recoveryPercent }}%</strong>
|
||||
</div>
|
||||
<div class="health-list__row">
|
||||
<span class="health-list__icon">
|
||||
<ShieldCheck :size="21" />
|
||||
</span>
|
||||
<span>{{ phone.t('Apps.health.currentHealth') }}</span>
|
||||
<strong>{{ healthPercent }}%</strong>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<template v-else-if="activeTab === 'trends'">
|
||||
@@ -568,10 +525,6 @@ onBeforeUnmount(() => {
|
||||
}}
|
||||
</SkyButton>
|
||||
|
||||
<p class="health-privacy-note">
|
||||
<ShieldCheck :size="22" aria-hidden="true" />
|
||||
<span>{{ phone.t('Apps.health.medicalId.privacy') }}</span>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<p v-if="actionError" class="health-action-error" role="alert">
|
||||
@@ -890,15 +843,6 @@ onBeforeUnmount(() => {
|
||||
color: var(--health-orange);
|
||||
}
|
||||
|
||||
.health-list__row .health-value--good {
|
||||
color: var(--health-green);
|
||||
}
|
||||
|
||||
.health-list__row .health-value--fair {
|
||||
color: var(--health-orange);
|
||||
}
|
||||
|
||||
.health-list__row .health-value--low,
|
||||
.health-list__row .health-value--accent {
|
||||
color: var(--health-accent);
|
||||
}
|
||||
@@ -1121,25 +1065,6 @@ onBeforeUnmount(() => {
|
||||
gap: 9px;
|
||||
}
|
||||
|
||||
.health-privacy-note {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin: 0;
|
||||
padding: 14px 16px;
|
||||
border: calc(var(--sky-hairline-scale) * 1px) solid var(--health-panel-border);
|
||||
border-radius: 18px;
|
||||
background: var(--health-panel);
|
||||
color: var(--sky-muted);
|
||||
font-size: 13px;
|
||||
line-height: 1.35;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.health-privacy-note svg {
|
||||
flex: none;
|
||||
color: var(--health-green);
|
||||
}
|
||||
|
||||
.health-form {
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
|
||||
Reference in New Issue
Block a user