You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
819 lines
21 KiB
819 lines
21 KiB
import { Ionicons } from '@expo/vector-icons';
|
|
import { useRouter } from 'expo-router';
|
|
import { useState } from 'react';
|
|
import {
|
|
ActivityIndicator,
|
|
Pressable,
|
|
ScrollView,
|
|
StyleSheet,
|
|
Text,
|
|
TextInput,
|
|
View,
|
|
} from 'react-native';
|
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
import { useTalentRecommendation } from '../../src/hooks';
|
|
import { ENTERPRISE_DISPLAY_NAME } from '../../src/constants';
|
|
import {colors, spacing} from '../../src/theme';
|
|
import type { TalentCandidate, TalentRecommendationTab } from '../../src/types';
|
|
|
|
/* ===================== 子组件:顶部导航 ===================== */
|
|
|
|
function Header({
|
|
companyName,
|
|
onBack,
|
|
searching,
|
|
onToggleSearch,
|
|
}: {
|
|
companyName: string;
|
|
onBack: () => void;
|
|
/** 是否处于搜索态;加载中/加载失败时没有数据可搜,不传即不渲染搜索按钮 */
|
|
searching?: boolean;
|
|
onToggleSearch?: () => void;
|
|
}) {
|
|
return (
|
|
<View style={styles.header}>
|
|
<Pressable style={styles.headerBtn} onPress={onBack} hitSlop={8}>
|
|
<Ionicons name="chevron-back" size={24} color={colors.textPrimary} />
|
|
</Pressable>
|
|
<View style={styles.headerCenter}>
|
|
<Text style={styles.headerCompany}>{companyName}</Text>
|
|
<Text style={styles.headerTitle}>人才推荐</Text>
|
|
</View>
|
|
<View style={styles.headerActions}>
|
|
{/* 原先这里是「搜索 + 漏斗」两个图标:漏斗没有对应页面,先摘掉;
|
|
搜索改成真的关键词过滤(姓名 / 职位 / 技能 / 学校) */}
|
|
{onToggleSearch && (
|
|
<Pressable style={styles.headerActionBtn} onPress={onToggleSearch} hitSlop={6}>
|
|
<Ionicons
|
|
name={searching ? 'close' : 'search-outline'}
|
|
size={22}
|
|
color={colors.textPrimary}
|
|
/>
|
|
</Pressable>
|
|
)}
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:搜索栏 ===================== */
|
|
|
|
function SearchBar({
|
|
value,
|
|
onChange,
|
|
onClear,
|
|
}: {
|
|
value: string;
|
|
onChange: (text: string) => void;
|
|
onClear: () => void;
|
|
}) {
|
|
return (
|
|
<View style={styles.searchBar}>
|
|
<Ionicons name="search-outline" size={18} color={colors.textHint} />
|
|
<TextInput
|
|
style={styles.searchInput}
|
|
value={value}
|
|
onChangeText={onChange}
|
|
placeholder="搜索姓名 / 职位 / 技能"
|
|
placeholderTextColor={colors.textHint}
|
|
autoFocus
|
|
returnKeyType="search"
|
|
/>
|
|
{value !== '' && (
|
|
<Pressable onPress={onClear} hitSlop={8}>
|
|
<Ionicons name="close-circle" size={16} color={colors.textHint} />
|
|
</Pressable>
|
|
)}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:Tab 栏 ===================== */
|
|
|
|
function TabBar({
|
|
tabs,
|
|
active,
|
|
onChange,
|
|
}: {
|
|
tabs: TalentRecommendationTab[];
|
|
active: string;
|
|
onChange: (key: string) => void;
|
|
}) {
|
|
return (
|
|
<View style={styles.tabBar}>
|
|
{tabs.map((tab) => {
|
|
const isActive = tab.key === active;
|
|
return (
|
|
<Pressable key={tab.key} style={styles.tabItem} onPress={() => onChange(tab.key)}>
|
|
<Text style={[styles.tabLabel, isActive && styles.tabLabelActive]}>
|
|
{tab.label}
|
|
<Text style={[styles.tabCount, isActive && styles.tabCountActive]}>({tab.count})</Text>
|
|
</Text>
|
|
<View style={[styles.tabIndicator, isActive && styles.tabIndicatorActive]} />
|
|
</Pressable>
|
|
);
|
|
})}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:今日推荐头部 ===================== */
|
|
|
|
/**
|
|
* 今日推荐头部。
|
|
* 原先右上角有「查看人才市场报告」入口,但项目没有该页面 —— 按「不留假交互」的口径摘掉。
|
|
*/
|
|
function TodayHeader({ count }: { count: number }) {
|
|
return (
|
|
<View style={styles.todayHeader}>
|
|
<View>
|
|
<Text style={styles.todayTitle}>今日为你推荐</Text>
|
|
<View style={styles.todayCountRow}>
|
|
<Text style={styles.todayCount}>{count}</Text>
|
|
<Text style={styles.todayUnit}>位优质候选人</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:候选人卡片 ===================== */
|
|
|
|
function CandidateCard({ candidate }: { candidate: TalentCandidate }) {
|
|
const router = useRouter();
|
|
return (
|
|
<View style={styles.candidateCard}>
|
|
<View style={styles.candidateHeader}>
|
|
<View style={styles.avatar}>
|
|
<Text style={styles.avatarText}>{candidate.avatar}</Text>
|
|
</View>
|
|
<View style={styles.candidateInfo}>
|
|
<View style={styles.nameRow}>
|
|
<Text style={styles.candidateName}>{candidate.name}</Text>
|
|
<View style={styles.matchTag}>
|
|
<Text style={styles.matchTagText}>{candidate.matchPercent}%</Text>
|
|
</View>
|
|
</View>
|
|
<Text style={styles.candidateTitle}>{candidate.title}</Text>
|
|
<Text style={styles.candidateMeta}>
|
|
{candidate.age}岁 · {candidate.school}
|
|
</Text>
|
|
<Text style={styles.candidateSalary}>{candidate.expectedSalary}</Text>
|
|
</View>
|
|
</View>
|
|
|
|
<View style={styles.scoreRow}>
|
|
<Text style={styles.scoreLabel}>综合评分</Text>
|
|
<Text style={styles.scoreValue}>{candidate.overallScore}</Text>
|
|
<Text style={styles.percentileText}>超越 {candidate.percentile}% 候选人</Text>
|
|
</View>
|
|
|
|
<View style={styles.tagRow}>
|
|
{candidate.tags.map((tag) => (
|
|
<View key={tag} style={styles.tag}>
|
|
<Text style={styles.tagText}>{tag}</Text>
|
|
</View>
|
|
))}
|
|
</View>
|
|
|
|
<View style={styles.candidateActions}>
|
|
<Pressable style={styles.chatBtn} onPress={() => router.navigate('/enterprise-chat')}>
|
|
<Text style={styles.chatBtnText}>发起沟通</Text>
|
|
</Pressable>
|
|
<Pressable style={styles.detailBtn} onPress={() => router.push('/candidate-detail?id=' + candidate.id)}>
|
|
<Text style={styles.detailBtnText}>查看详情</Text>
|
|
</Pressable>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:人才池数据 ===================== */
|
|
|
|
function TalentPool({ stats, actions, onPressAction }: { stats: { id: string; value: string; label: string }[]; actions: PoolActionItem[]; onPressAction: (action: PoolActionItem) => void }) {
|
|
return (
|
|
<View style={styles.poolCard}>
|
|
<Text style={styles.poolTitle}>人才池数据</Text>
|
|
<View style={styles.poolStatsRow}>
|
|
{stats.map((stat) => (
|
|
<View key={stat.id} style={styles.poolStat}>
|
|
<Text style={styles.poolStatValue}>{stat.value}</Text>
|
|
<Text style={styles.poolStatLabel}>{stat.label}</Text>
|
|
</View>
|
|
))}
|
|
</View>
|
|
<View style={styles.poolActions}>
|
|
{actions.map((action) => (
|
|
<Pressable key={action.label} style={styles.poolAction} onPress={() => onPressAction(action)}>
|
|
<Ionicons name={action.icon as keyof typeof Ionicons.glyphMap} size={20} color={colors.textSecondary} />
|
|
<Text style={styles.poolActionText}>{action.label}</Text>
|
|
</Pressable>
|
|
))}
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 子组件:空状态 ===================== */
|
|
|
|
function EmptyState({
|
|
actions,
|
|
dataReference,
|
|
onFilter,
|
|
}: {
|
|
actions: string[];
|
|
dataReference: { label: string; value: string }[];
|
|
onFilter: () => void;
|
|
}) {
|
|
return (
|
|
<View style={styles.emptyCard}>
|
|
<View style={styles.emptyIconWrap}>
|
|
<Ionicons name="people-outline" size={48} color={colors.primary} />
|
|
</View>
|
|
<Text style={styles.emptyTitle}>暂无匹配候选人</Text>
|
|
<Text style={styles.emptySubtitle}>当前筛选条件下没有较适合的候选人,试试调整筛选条件</Text>
|
|
<Pressable style={styles.emptyBtn} onPress={onFilter} hitSlop={6}>
|
|
<Text style={styles.emptyBtnText}>调整筛选条件</Text>
|
|
</Pressable>
|
|
|
|
<View style={styles.actionCard}>
|
|
<View style={styles.actionHeader}>
|
|
<Ionicons name="bulb-outline" size={18} color={colors.compareOrange} />
|
|
<Text style={styles.actionTitle}>推荐操作</Text>
|
|
</View>
|
|
{actions.map((action, index) => (
|
|
<View key={index} style={styles.actionRow}>
|
|
<View style={styles.actionDot} />
|
|
<Text style={styles.actionText}>{action}</Text>
|
|
</View>
|
|
))}
|
|
</View>
|
|
|
|
<View style={styles.dataCard}>
|
|
<Text style={styles.dataTitle}>数据参考</Text>
|
|
{dataReference.map((item, index) => (
|
|
<View key={index} style={styles.dataRow}>
|
|
<Ionicons name="trending-up-outline" size={16} color={colors.success} />
|
|
<Text style={styles.dataText}>
|
|
{item.label}
|
|
<Text style={styles.dataValue}> {item.value}</Text>
|
|
</Text>
|
|
</View>
|
|
))}
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/* ===================== 页面 ===================== */
|
|
|
|
/**
|
|
* 人才池快捷操作项。`route` 必填 —— 每个入口都必须有落点,
|
|
* 批量发邀请 / 批量发Offer / 群发通知三项因项目内没有对应页面,已摘掉。
|
|
*/
|
|
type PoolActionItem = { icon: string; label: string; route: string };
|
|
|
|
const POOL_ACTIONS: PoolActionItem[] = [
|
|
{ icon: 'calendar-outline', label: '面试评分', route: '/interview-score' },
|
|
];
|
|
|
|
export default function TalentRecommendationScreen() {
|
|
const router = useRouter();
|
|
const {
|
|
recommendation,
|
|
activeTab,
|
|
setActiveTab,
|
|
keyword,
|
|
setKeyword,
|
|
candidates,
|
|
isEmptySearch,
|
|
loading,
|
|
error,
|
|
refresh,
|
|
} = useTalentRecommendation();
|
|
const [searching, setSearching] = useState(false);
|
|
|
|
const toggleSearch = () => {
|
|
if (searching) setKeyword('');
|
|
setSearching((prev) => !prev);
|
|
};
|
|
|
|
/** 人才池快捷操作:每个入口都由数据保证有落点,页面不再需要「没页面就弹提示」的分支 */
|
|
const handlePoolAction = (action: PoolActionItem) => {
|
|
router.push(action.route);
|
|
};
|
|
|
|
/** 空状态里的「调整筛选条件」:把筛选真正复位(切回全部 + 清关键词) */
|
|
const handleFilter = () => {
|
|
setActiveTab('all');
|
|
setKeyword('');
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<SafeAreaView style={styles.container} edges={['top']}>
|
|
<Header companyName={ENTERPRISE_DISPLAY_NAME} onBack={() => router.back()} />
|
|
<View style={styles.center}>
|
|
<ActivityIndicator size="large" color={colors.primary} />
|
|
</View>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
if (error || !recommendation) {
|
|
return (
|
|
<SafeAreaView style={styles.container} edges={['top']}>
|
|
<Header companyName={ENTERPRISE_DISPLAY_NAME} onBack={() => router.back()} />
|
|
<View style={styles.center}>
|
|
<Ionicons name="alert-circle-outline" size={40} color={colors.danger} />
|
|
<Text style={styles.tip}>加载失败</Text>
|
|
<Pressable style={styles.retryBtn} onPress={refresh}>
|
|
<Text style={styles.retryText}>重试</Text>
|
|
</Pressable>
|
|
</View>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
|
<Header
|
|
companyName={recommendation.companyName}
|
|
onBack={() => router.back()}
|
|
searching={searching}
|
|
onToggleSearch={toggleSearch}
|
|
/>
|
|
<TabBar tabs={recommendation.tabs} active={activeTab} onChange={setActiveTab} />
|
|
{searching && (
|
|
<SearchBar value={keyword} onChange={setKeyword} onClear={() => setKeyword('')} />
|
|
)}
|
|
<ScrollView contentContainerStyle={styles.body} showsVerticalScrollIndicator={false}>
|
|
{recommendation.hasData ? (
|
|
<View>
|
|
<TodayHeader count={recommendation.todayCount} />
|
|
{candidates.length === 0 ? (
|
|
<View style={styles.emptyList}>
|
|
<Ionicons name="search-outline" size={32} color={colors.textHint} />
|
|
<Text style={styles.emptyListText}>
|
|
{isEmptySearch
|
|
? `没有匹配「${keyword.trim()}」的候选人`
|
|
: '这个筛选条件下暂时没有候选人'}
|
|
</Text>
|
|
<Pressable style={styles.emptyListBtn} onPress={handleFilter} hitSlop={6}>
|
|
<Text style={styles.emptyListBtnText}>重置筛选</Text>
|
|
</Pressable>
|
|
</View>
|
|
) : (
|
|
candidates.map((candidate) => (
|
|
<CandidateCard key={candidate.id} candidate={candidate} />
|
|
))
|
|
)}
|
|
<TalentPool
|
|
stats={recommendation.poolStats}
|
|
actions={POOL_ACTIONS}
|
|
onPressAction={handlePoolAction}
|
|
/>
|
|
</View>
|
|
) : (
|
|
<EmptyState
|
|
actions={recommendation.recommendedActions}
|
|
dataReference={recommendation.dataReference}
|
|
onFilter={handleFilter}
|
|
/>
|
|
)}
|
|
</ScrollView>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: { flex: 1, backgroundColor: colors.pageBg },
|
|
header: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
paddingHorizontal: 12,
|
|
paddingVertical: 12,
|
|
backgroundColor: colors.white,
|
|
},
|
|
headerBtn: { width: 40, alignItems: 'center' },
|
|
headerCenter: { alignItems: 'center' },
|
|
headerCompany: { fontSize: 12, color: colors.textSecondary },
|
|
headerTitle: { fontSize: 17, fontWeight: '700', color: colors.textPrimary, marginTop: 2 },
|
|
headerActions: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
},
|
|
headerActionBtn: {
|
|
width: 36,
|
|
alignItems: 'center',
|
|
},
|
|
searchBar: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 8,
|
|
backgroundColor: colors.white,
|
|
paddingHorizontal: spacing.framework,
|
|
paddingBottom: 10,
|
|
},
|
|
searchInput: {
|
|
flex: 1,
|
|
backgroundColor: colors.pageBg,
|
|
borderRadius: 10,
|
|
paddingHorizontal: 12,
|
|
paddingVertical: 8,
|
|
fontSize: 14,
|
|
color: colors.textPrimary,
|
|
},
|
|
emptyList: {
|
|
alignItems: 'center',
|
|
gap: 10,
|
|
padding: 8,
|
|
backgroundColor: colors.white,
|
|
borderRadius: 8,
|
|
},
|
|
emptyListText: { fontSize: 13, color: colors.textSecondary },
|
|
emptyListBtn: {
|
|
paddingHorizontal: 18,
|
|
paddingVertical: 8,
|
|
borderRadius: 16,
|
|
backgroundColor: colors.primaryLight,
|
|
},
|
|
emptyListBtnText: { fontSize: 13, fontWeight: '600', color: colors.primaryText },
|
|
tabBar: {
|
|
flexDirection: 'row',
|
|
backgroundColor: colors.white,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: colors.borderLight,
|
|
},
|
|
tabItem: {
|
|
flex: 1,
|
|
alignItems: 'center',
|
|
paddingTop: 8,
|
|
},
|
|
tabLabel: {
|
|
fontSize: 14,
|
|
color: colors.textSecondary,
|
|
paddingBottom: 8,
|
|
},
|
|
tabLabelActive: {
|
|
color: colors.textPrimary,
|
|
fontWeight: '600',
|
|
},
|
|
tabCount: {
|
|
fontSize: 12,
|
|
color: colors.textHint,
|
|
marginLeft: 2,
|
|
},
|
|
tabCountActive: {
|
|
color: colors.primary,
|
|
},
|
|
tabIndicator: {
|
|
height: 2,
|
|
width: 24,
|
|
borderRadius: 1,
|
|
backgroundColor: 'transparent',
|
|
marginBottom: -1,
|
|
},
|
|
tabIndicatorActive: {
|
|
backgroundColor: colors.primary,
|
|
},
|
|
body: {
|
|
padding: 8,
|
|
paddingBottom: 24,
|
|
},
|
|
center: {
|
|
flex: 1,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
gap: 12,
|
|
},
|
|
tip: { fontSize: 14, color: colors.textSecondary },
|
|
retryBtn: {
|
|
marginTop: 8,
|
|
paddingHorizontal: 24,
|
|
paddingVertical: 8,
|
|
borderRadius: 18,
|
|
backgroundColor: colors.primaryBtn,
|
|
},
|
|
retryText: { color: colors.white, fontSize: 13, fontWeight: '600' },
|
|
todayHeader: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
marginBottom: 12,
|
|
},
|
|
todayTitle: {
|
|
fontSize: 13,
|
|
color: colors.textSecondary,
|
|
},
|
|
todayCountRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'baseline',
|
|
marginTop: 2,
|
|
},
|
|
todayCount: {
|
|
fontSize: 28,
|
|
fontWeight: '800',
|
|
color: colors.primary,
|
|
},
|
|
todayUnit: {
|
|
fontSize: 13,
|
|
color: colors.textSecondary,
|
|
marginLeft: 4,
|
|
},
|
|
reportBtn: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
backgroundColor: colors.primaryLight,
|
|
borderRadius: 14,
|
|
paddingHorizontal: 10,
|
|
paddingVertical: 6,
|
|
},
|
|
reportBtnText: {
|
|
fontSize: 12,
|
|
color: colors.primary,
|
|
fontWeight: '600',
|
|
marginRight: 2,
|
|
},
|
|
candidateCard: {
|
|
backgroundColor: colors.white,
|
|
borderRadius: 8,
|
|
padding: 8,
|
|
marginBottom: 8,
|
|
},
|
|
candidateHeader: {
|
|
flexDirection: 'row',
|
|
},
|
|
avatar: {
|
|
width: 52,
|
|
height: 52,
|
|
borderRadius: 26,
|
|
backgroundColor: colors.primaryLight,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginRight: 12,
|
|
},
|
|
avatarText: {
|
|
fontSize: 20,
|
|
fontWeight: '700',
|
|
color: colors.primary,
|
|
},
|
|
candidateInfo: {
|
|
flex: 1,
|
|
},
|
|
nameRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
},
|
|
candidateName: {
|
|
fontSize: 16,
|
|
fontWeight: '700',
|
|
color: colors.textPrimary,
|
|
},
|
|
matchTag: {
|
|
backgroundColor: colors.successLight,
|
|
borderRadius: 10,
|
|
paddingHorizontal: 8,
|
|
paddingVertical: 2,
|
|
},
|
|
matchTagText: {
|
|
fontSize: 12,
|
|
fontWeight: '800',
|
|
color: colors.successText,
|
|
},
|
|
candidateTitle: {
|
|
fontSize: 13,
|
|
color: colors.textSecondary,
|
|
marginTop: 2,
|
|
},
|
|
candidateMeta: {
|
|
fontSize: 12,
|
|
color: colors.textHint,
|
|
marginTop: 4,
|
|
},
|
|
candidateSalary: {
|
|
fontSize: 13,
|
|
color: colors.action,
|
|
fontWeight: '600',
|
|
marginTop: 4,
|
|
},
|
|
scoreRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
marginTop: 12,
|
|
},
|
|
scoreLabel: {
|
|
fontSize: 13,
|
|
color: colors.textSecondary,
|
|
},
|
|
scoreValue: {
|
|
fontSize: 14,
|
|
fontWeight: '800',
|
|
color: colors.primary,
|
|
marginLeft: 6,
|
|
},
|
|
percentileText: {
|
|
fontSize: 12,
|
|
color: colors.textHint,
|
|
marginLeft: 8,
|
|
},
|
|
tagRow: {
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
gap: 8,
|
|
marginTop: 10,
|
|
},
|
|
tag: {
|
|
backgroundColor: colors.pageBg,
|
|
borderRadius: 6,
|
|
paddingHorizontal: 8,
|
|
paddingVertical: 3,
|
|
},
|
|
tagText: {
|
|
fontSize: 11,
|
|
color: colors.textSecondary,
|
|
},
|
|
candidateActions: {
|
|
flexDirection: 'row',
|
|
marginTop: 14,
|
|
gap: 10,
|
|
},
|
|
chatBtn: {
|
|
flex: 1,
|
|
backgroundColor: colors.primaryBtn,
|
|
borderRadius: 18,
|
|
paddingVertical: 10,
|
|
alignItems: 'center',
|
|
},
|
|
chatBtnText: {
|
|
color: colors.white,
|
|
fontSize: 14,
|
|
fontWeight: '600',
|
|
},
|
|
detailBtn: {
|
|
flex: 1,
|
|
backgroundColor: colors.primaryLight,
|
|
borderRadius: 18,
|
|
paddingVertical: 10,
|
|
alignItems: 'center',
|
|
},
|
|
detailBtnText: {
|
|
color: colors.primary,
|
|
fontSize: 14,
|
|
fontWeight: '600',
|
|
},
|
|
poolCard: {
|
|
backgroundColor: colors.white,
|
|
borderRadius: 8,
|
|
padding: 8,
|
|
marginTop: 8,
|
|
},
|
|
poolTitle: {
|
|
fontSize: 15,
|
|
fontWeight: '700',
|
|
color: colors.textPrimary,
|
|
marginBottom: 12,
|
|
},
|
|
poolStatsRow: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
marginBottom: 16,
|
|
},
|
|
poolStat: {
|
|
flex: 1,
|
|
alignItems: 'center',
|
|
},
|
|
poolStatValue: {
|
|
fontSize: 18,
|
|
fontWeight: '800',
|
|
color: colors.textPrimary,
|
|
},
|
|
poolStatLabel: {
|
|
fontSize: 11,
|
|
color: colors.textSecondary,
|
|
marginTop: 4,
|
|
},
|
|
poolActions: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-around',
|
|
paddingTop: 14,
|
|
borderTopWidth: 1,
|
|
borderTopColor: colors.borderLight,
|
|
},
|
|
poolAction: {
|
|
alignItems: 'center',
|
|
},
|
|
poolActionText: {
|
|
fontSize: 11,
|
|
color: colors.textSecondary,
|
|
marginTop: 4,
|
|
},
|
|
emptyCard: {
|
|
backgroundColor: colors.white,
|
|
borderRadius: 8,
|
|
padding: 8,
|
|
alignItems: 'center',
|
|
},
|
|
emptyIconWrap: {
|
|
width: 88,
|
|
height: 88,
|
|
borderRadius: 44,
|
|
backgroundColor: colors.primaryLight,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginBottom: 16,
|
|
},
|
|
emptyTitle: {
|
|
fontSize: 18,
|
|
fontWeight: '700',
|
|
color: colors.textPrimary,
|
|
marginBottom: 8,
|
|
},
|
|
emptySubtitle: {
|
|
fontSize: 13,
|
|
color: colors.textSecondary,
|
|
textAlign: 'center',
|
|
marginBottom: 20,
|
|
},
|
|
emptyBtn: {
|
|
backgroundColor: colors.primaryBtn,
|
|
paddingHorizontal: 32,
|
|
paddingVertical: 12,
|
|
borderRadius: 24,
|
|
marginBottom: 24,
|
|
},
|
|
emptyBtnText: {
|
|
color: colors.white,
|
|
fontSize: 15,
|
|
fontWeight: '600',
|
|
},
|
|
actionCard: {
|
|
width: '100%',
|
|
backgroundColor: colors.actionLight,
|
|
borderRadius: 8,
|
|
padding: 8,
|
|
marginBottom: 8,
|
|
alignItems: 'flex-start',
|
|
},
|
|
actionHeader: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
marginBottom: 12,
|
|
},
|
|
actionTitle: {
|
|
fontSize: 14,
|
|
fontWeight: '700',
|
|
color: colors.actionText,
|
|
marginLeft: 8,
|
|
},
|
|
actionRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'flex-start',
|
|
marginBottom: 10,
|
|
},
|
|
actionDot: {
|
|
width: 6,
|
|
height: 6,
|
|
borderRadius: 3,
|
|
backgroundColor: colors.action,
|
|
marginTop: 6,
|
|
marginRight: 10,
|
|
},
|
|
actionText: {
|
|
flex: 1,
|
|
fontSize: 13,
|
|
color: colors.textPrimary,
|
|
lineHeight: 18,
|
|
},
|
|
dataCard: {
|
|
width: '100%',
|
|
backgroundColor: colors.primaryLight,
|
|
borderRadius: 8,
|
|
padding: 8,
|
|
alignItems: 'flex-start',
|
|
},
|
|
dataTitle: {
|
|
fontSize: 14,
|
|
fontWeight: '700',
|
|
color: colors.primaryText,
|
|
marginBottom: 12,
|
|
},
|
|
dataRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'flex-start',
|
|
marginBottom: 10,
|
|
},
|
|
dataText: {
|
|
flex: 1,
|
|
fontSize: 13,
|
|
color: colors.textPrimary,
|
|
marginLeft: 10,
|
|
lineHeight: 18,
|
|
},
|
|
dataValue: {
|
|
color: colors.success,
|
|
fontWeight: '700',
|
|
},
|
|
});
|
|
|