|
1 | | -import { PlayerFinalStats } from "@/lib/api-types-generated"; |
2 | | -import { FALLBACK_AVATAR_URL } from "@/lib/constants"; |
3 | | -import { playersData } from "@/lib/mockData"; |
4 | | -import { StatisticsCardProps } from "./StatisticsCard"; |
5 | | -import { formatMs } from "@/lib/utils"; |
6 | | -import { Badge } from "@/components/ui/badge"; |
7 | | -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; |
8 | | -import StatisticsHeading from "./StatisticsHeading"; |
9 | | -import StatisticsRows from "./StatisticsRows"; |
10 | | -import { Skeleton } from "@/components/ui/skeleton"; |
11 | | -import StatisticsGameReview from "./StatisticsGameReview"; |
12 | | -import { Share } from "@/components/icons"; |
| 1 | +import { PlayerFinalStats } from '@/lib/api-types-generated'; |
| 2 | +import { FALLBACK_AVATAR_URL } from '@/lib/constants'; |
| 3 | +import { playersData } from '@/lib/mockData'; |
| 4 | +import { StatisticsCardProps } from './StatisticsCard'; |
| 5 | +import { formatMs, formatMsToHoursMins } from '@/lib/utils'; |
| 6 | +import { Badge } from '@/components/ui/badge'; |
| 7 | +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; |
| 8 | +import StatisticsHeading from './StatisticsHeading'; |
| 9 | +import StatisticsRows from './StatisticsRows'; |
| 10 | +import { Skeleton } from '@/components/ui/skeleton'; |
| 11 | +import StatisticsGameReview from './StatisticsGameReview'; |
| 12 | +import { Share } from '@/components/icons'; |
13 | 13 |
|
14 | 14 | type PlayerSectionProps = { |
15 | | - data: PlayerFinalStats & { |
16 | | - placement: number; |
17 | | - monopolies_amount: number; |
18 | | - buildings_amount: number; |
19 | | - }; |
20 | | -} |
| 15 | + data: PlayerFinalStats & { |
| 16 | + placement: number; |
| 17 | + monopolies_amount: number; |
| 18 | + buildings_amount: number; |
| 19 | + }; |
| 20 | +}; |
21 | 21 |
|
22 | 22 | function StatisticsPlayerSection({ data }: PlayerSectionProps) { |
23 | | - const color = playersData.find((p) => p.id === data.player_id)?.color || 'white'; |
24 | | - const avatar = playersData.find((p) => p.id === data.player_id)?.avatar_link || FALLBACK_AVATAR_URL; |
25 | | - const filteredData = { |
26 | | - total_score: data.total_score, |
27 | | - games_completed: data.games_completed, |
28 | | - games_dropped: data.games_dropped, |
29 | | - longest_game_hours: data.longest_game_hours, |
30 | | - shortest_game_hours: data.shortest_game_hours, |
31 | | - cards_amount: data.cards_amount, |
32 | | - hours_played: data.hours_played, |
33 | | - buildings_amount: data.buildings_amount, |
34 | | - monopolies_amount: data.monopolies_amount, |
35 | | - }; |
| 23 | + const color = playersData.find(p => p.id === data.player_id)?.color || 'white'; |
| 24 | + const avatar = playersData.find(p => p.id === data.player_id)?.avatar_link || FALLBACK_AVATAR_URL; |
36 | 25 |
|
37 | | - const keyToProps = (key: string): Omit<StatisticsCardProps, 'value'> => { |
38 | | - switch (key) { |
39 | | - case 'total_score': { |
40 | | - const totalScoreText = |
41 | | - data.placement === 1 |
42 | | - ? 'Очков — самый\nбогатый' |
43 | | - : data.placement === playersData.length |
44 | | - ? 'Очков — \nБанкрот!' |
45 | | - : 'Очков\nполучено'; |
46 | | - return { |
47 | | - text: totalScoreText, |
48 | | - icon: <Share className="size-[26px]" />, |
49 | | - order: 1, |
50 | | - }; |
51 | | - } |
52 | | - case 'longest_game_hours': |
53 | | - return { |
54 | | - text: `Самая\nдлинная игра`, |
55 | | - modifiedValue: formatMs(data[key] * 1000), |
56 | | - order: 4, |
57 | | - }; |
58 | | - case 'shortest_game_hours': |
59 | | - return { |
60 | | - text: `Самая\nкороткая игра`, |
61 | | - modifiedValue: formatMs(data[key] * 1000), |
62 | | - order: 9, |
63 | | - }; |
64 | | - case 'hours_played': |
65 | | - return { |
66 | | - text: `Наиграно в игры\nна ивенте`, |
67 | | - modifiedValue: formatMs(data[key] * 1000), |
68 | | - order: 6, |
69 | | - }; |
70 | | - case 'games_completed': |
71 | | - return { text: `Игр\nпройдено`, order: 3, }; |
72 | | - case 'games_dropped': |
73 | | - return { text: `Игр\nдропнуто`, order: 7, }; |
74 | | - case 'cards_amount': |
75 | | - return { text: `Карточек\nзаролено`, order: 5, }; |
76 | | - case 'buildings_amount': |
77 | | - return { text: `Зданий\nна карте`, order: 2, }; |
78 | | - case 'monopolies_amount': |
79 | | - return { text: `Монополий\nпостроено`, order: 8, }; |
80 | | - default: |
81 | | - return { text: '', order: 0, }; |
82 | | - } |
83 | | - }; |
| 26 | + const keyToProps = (key: string): Omit<StatisticsCardProps, 'value'> | null => { |
| 27 | + const dataKey = key as keyof PlayerSectionProps['data']; |
| 28 | + switch (dataKey) { |
| 29 | + case 'total_score': { |
| 30 | + const totalScoreText = |
| 31 | + data.placement === 1 |
| 32 | + ? 'Очков — самый\nбогатый' |
| 33 | + : data.placement === playersData.length |
| 34 | + ? 'Очков — \nБанкрот!' |
| 35 | + : 'Очков\nполучено'; |
| 36 | + return { |
| 37 | + text: totalScoreText, |
| 38 | + icon: <Share className="size-[26px]" />, |
| 39 | + order: 1, |
| 40 | + }; |
| 41 | + } |
| 42 | + case 'longest_game_hours': |
| 43 | + return { |
| 44 | + text: `Самая\nдлинная игра`, |
| 45 | + modifiedValue: formatMsToHoursMins(data[dataKey] * 1000), |
| 46 | + order: 4, |
| 47 | + }; |
| 48 | + case 'shortest_game_hours': |
| 49 | + return { |
| 50 | + text: `Самая\nкороткая игра`, |
| 51 | + modifiedValue: formatMsToHoursMins(data[dataKey] * 1000), |
| 52 | + order: 9, |
| 53 | + }; |
| 54 | + case 'hours_played': |
| 55 | + return { |
| 56 | + text: `Наиграно в игры\nна ивенте`, |
| 57 | + modifiedValue: formatMs(data[dataKey] * 1000), |
| 58 | + order: 6, |
| 59 | + }; |
| 60 | + case 'games_completed': |
| 61 | + return { text: `Игр\nпройдено`, order: 3 }; |
| 62 | + case 'games_dropped': |
| 63 | + return { text: `Игр\nдропнуто`, order: 7 }; |
| 64 | + case 'cards_amount': |
| 65 | + return { text: `Карточек\nзаролено`, order: 5 }; |
| 66 | + case 'buildings_amount': |
| 67 | + return { text: `Зданий\nна карте`, order: 2 }; |
| 68 | + case 'monopolies_amount': |
| 69 | + return { text: `Монополий\nпостроено`, order: 8 }; |
| 70 | + default: |
| 71 | + return null; |
| 72 | + } |
| 73 | + }; |
84 | 74 |
|
85 | | - return ( |
86 | | - <div className="space-y-[30px]"> |
87 | | - <div className="space-y-[15px]"> |
88 | | - <Badge |
89 | | - className="flex py-1 uppercase font-roboto-wide-semibold-italic leading-[19px] border-0 mx-auto" |
90 | | - style={{ backgroundColor: color }} |
91 | | - > |
92 | | - {data.placement} место |
93 | | - </Badge> |
94 | | - <div className="flex gap-2 justify-center items-center"> |
95 | | - <div className="relative"> |
96 | | - <Avatar className="size-[56px] overflow-auto"> |
97 | | - <AvatarImage src={avatar} /> |
98 | | - <AvatarFallback className="uppercase">{data.username.slice(0, 2)}</AvatarFallback> |
99 | | - </Avatar> |
100 | | - </div> |
101 | | - <StatisticsHeading> |
102 | | - {data.username} |
103 | | - </StatisticsHeading> |
104 | | - </div> |
105 | | - </div> |
| 75 | + return ( |
| 76 | + <div className="space-y-[30px]"> |
| 77 | + <div className="space-y-[15px]"> |
| 78 | + <Badge |
| 79 | + className="flex py-1 uppercase font-roboto-wide-semibold-italic leading-[19px] border-0 mx-auto" |
| 80 | + style={{ backgroundColor: color }} |
| 81 | + > |
| 82 | + {data.placement} место |
| 83 | + </Badge> |
| 84 | + <div className="flex gap-2 justify-center items-center"> |
| 85 | + <div className="relative"> |
| 86 | + <Avatar className="size-[56px] overflow-auto"> |
| 87 | + <AvatarImage src={avatar} /> |
| 88 | + <AvatarFallback className="uppercase">{data.username.slice(0, 2)}</AvatarFallback> |
| 89 | + </Avatar> |
| 90 | + </div> |
| 91 | + <StatisticsHeading>{data.username}</StatisticsHeading> |
| 92 | + </div> |
| 93 | + </div> |
106 | 94 |
|
107 | | - <StatisticsRows data={filteredData} keyToProps={keyToProps} /> |
| 95 | + <StatisticsRows data={data} keyToProps={keyToProps} /> |
108 | 96 |
|
109 | | - <div className="space-y-[15px]"> |
110 | | - <div className="text-center font-roboto-wide-black-alt text-2xl leading-7">Лучший клип</div> |
111 | | - <div className="w-[500px] h-[281px] shrink-0 mx-auto"> |
112 | | - <Skeleton className="w-full h-full" /> |
113 | | - </div> |
114 | | - </div> |
| 97 | + <div className="space-y-[15px]"> |
| 98 | + <div className="text-center font-roboto-wide-black-alt text-2xl leading-7">Лучший клип</div> |
| 99 | + <div className="w-[500px] h-[281px] shrink-0 mx-auto"> |
| 100 | + <Skeleton className="w-full h-full" /> |
| 101 | + </div> |
| 102 | + </div> |
115 | 103 |
|
116 | | - {(data.best_rated_game && data.worst_rated_game) && ( |
117 | | - <div className="flex gap-[15px] justify-center"> |
118 | | - {data.best_rated_game && ( |
119 | | - <StatisticsGameReview data={data.best_rated_game} title="Лучшая игра" /> |
120 | | - )} |
121 | | - {data.worst_rated_game && ( |
122 | | - <StatisticsGameReview data={data.worst_rated_game} title="Худшая игра" /> |
123 | | - )} |
124 | | - </div> |
125 | | - )} |
126 | | - </div> |
127 | | - ) |
| 104 | + {data.best_rated_game && data.worst_rated_game && ( |
| 105 | + <div className="flex gap-[15px] justify-center"> |
| 106 | + {data.best_rated_game && ( |
| 107 | + <StatisticsGameReview data={data.best_rated_game} title="Лучшая игра" /> |
| 108 | + )} |
| 109 | + {data.worst_rated_game && ( |
| 110 | + <StatisticsGameReview data={data.worst_rated_game} title="Худшая игра" /> |
| 111 | + )} |
| 112 | + </div> |
| 113 | + )} |
| 114 | + </div> |
| 115 | + ); |
128 | 116 | } |
129 | 117 |
|
130 | 118 | export default StatisticsPlayerSection; |
0 commit comments