6224ef5980
- Implemented a new Growth page to track practice time and trends. - Added API integration for fetching review data. - Created components for displaying practice statistics and trends. - Updated navigation titles for the main index and growth pages. - Removed unused styles from the index page. - Introduced a Projects management page for adding and editing practice projects. - Developed a Record form for logging practice sessions with validation. - Added utility functions for date manipulation and duration formatting. - Implemented error handling and session management in the practice service. - Created unit tests for the practice service to ensure reliability.
34 lines
2.1 KiB
TypeScript
34 lines
2.1 KiB
TypeScript
import Taro from '@tarojs/taro'
|
|
import { Text, View } from '@tarojs/components'
|
|
import Button from '@taroify/core/button'
|
|
import '@taroify/core/button/style'
|
|
import type { PracticeRecord } from '../services/practice'
|
|
import { duration } from '../utils/dates'
|
|
|
|
export { Button }
|
|
|
|
export function PageHeading({ title, subtitle }: { title: string; subtitle: string }) {
|
|
return <View className='page-heading'><Text className='eyebrow'>BALLET ISLAND</Text><Text className='page-title'>{title}</Text><Text className='muted'>{subtitle}</Text></View>
|
|
}
|
|
export function LoadState({ loading, error, retry }: { loading: boolean; error: string; retry: () => unknown }) {
|
|
if (!loading && !error) return null
|
|
return <View className='panel status-panel'><Text className={error ? 'error-text' : 'muted'}>{loading ? '正在读取你的练习…' : error}</Text>{error && <Button size='small' variant='outlined' onClick={() => { void retry() }}>重新加载</Button>}</View>
|
|
}
|
|
export function Empty({ title, text }: { title: string; text: string }) {
|
|
return <View className='empty'><Text className='empty-title'>{title}</Text><Text className='muted'>{text}</Text></View>
|
|
}
|
|
export function RecordCards({ records }: { records: PracticeRecord[] }) {
|
|
return <View>{records.map(record => <View className='record-card' key={record.id}>
|
|
<View className='row'><Text className='record-name'>{record.projectName}</Text><Text className='record-duration'>{duration(record.minutes)}</Text></View>
|
|
<View className='row'><Text className='muted small'>{record.date}{record.archived ? ' · 已归档项目' : ''}</Text><Button size='small' variant='text' onClick={() => { void Taro.navigateTo({ url: `/pages/record/index?id=${record.id}` }) }}>更正</Button></View>
|
|
{record.note && <Text className='record-note'>{record.note}</Text>}
|
|
</View>)}</View>
|
|
}
|
|
|
|
/** Return to the actual originating page; deep-entry forms fall back to records. */
|
|
export async function returnToSource(message?: string) {
|
|
if (Taro.getCurrentPages().length > 1) await Taro.navigateBack()
|
|
else await Taro.switchTab({ url: '/pages/index/index' })
|
|
if (message) void Taro.showToast({ title: message, icon: 'none' })
|
|
}
|