feat: add project icon picker component and integrate with project management
- Implemented ProjectIconPicker component for selecting project icons. - Created ProjectIcon component to display selected icons. - Updated calendar page to remove unused PageHeading component. - Modified growth page to enhance UI and improve text clarity. - Refactored index page to include project icons and improve layout. - Added mine page for user profile management with avatar selection. - Introduced projects-sort page for managing project order with drag-and-drop functionality. - Enhanced projects page to allow icon selection when adding or editing projects. - Updated record page to streamline user experience and improve form actions. - Adjusted practice service to support project icon management. - Added tests for project saving functionality to ensure correct handling of icons. - Updated global type definitions to support SVG and PNG imports.
This commit is contained in:
@@ -7,15 +7,25 @@ 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 FormActions({ busy, cancelDisabled, saveDisabled, saveLabel = '保存', onCancel, onSave }: {
|
||||
busy: boolean
|
||||
cancelDisabled?: boolean
|
||||
saveDisabled?: boolean
|
||||
saveLabel?: string
|
||||
onCancel: () => unknown
|
||||
onSave: () => unknown
|
||||
}) {
|
||||
return <View className='form-actions'>
|
||||
<View className='form-action'><Button block variant='outlined' disabled={busy || cancelDisabled} onClick={() => { void onCancel() }}>取消</Button></View>
|
||||
<View className='form-action'><Button block color='primary' loading={busy} disabled={busy || saveDisabled} onClick={() => { void onSave() }}>{saveLabel}</Button></View>
|
||||
</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 Empty({ title }: { title: string }) {
|
||||
return <View className='empty'><Text className='empty-title'>{title}</Text></View>
|
||||
}
|
||||
export function RecordCards({ records }: { records: PracticeRecord[] }) {
|
||||
return <View>{records.map(record => <View className='record-card' key={record.id}>
|
||||
|
||||
Reference in New Issue
Block a user