@magmamath/students-features 1.3.23-rc.21 → 1.3.23-rc.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/commonjs/features/keyboard/constants/groups/currency.constants.js +3 -0
  2. package/dist/commonjs/features/keyboard/constants/groups/currency.constants.js.map +1 -1
  3. package/dist/commonjs/features/keyboard/constants/keyboard.helpers.js +2 -1
  4. package/dist/commonjs/features/keyboard/constants/keyboard.helpers.js.map +1 -1
  5. package/dist/commonjs/features/keyboard/constants/keys/units.constants.js +16 -0
  6. package/dist/commonjs/features/keyboard/constants/keys/units.constants.js.map +1 -1
  7. package/dist/commonjs/features/pmProgress/components/chart/chart.helpers.js +1 -0
  8. package/dist/commonjs/features/pmProgress/components/chart/chart.helpers.js.map +1 -1
  9. package/dist/commonjs/features/pmProgress/components/header/PmProgressHeader.js +19 -16
  10. package/dist/commonjs/features/pmProgress/components/header/PmProgressHeader.js.map +1 -1
  11. package/dist/commonjs/features/pmProgress/components/header/StartButton.js +10 -1
  12. package/dist/commonjs/features/pmProgress/components/header/StartButton.js.map +1 -1
  13. package/dist/commonjs/features/pmProgress/components/list/{ListCard.js → DomainCard.js} +11 -27
  14. package/dist/commonjs/features/pmProgress/components/list/DomainCard.js.map +1 -0
  15. package/dist/commonjs/features/pmProgress/components/list/Fluency.js +3 -3
  16. package/dist/commonjs/features/pmProgress/components/list/Fluency.js.map +1 -1
  17. package/dist/commonjs/features/pmProgress/components/list/PmDomainList.js +3 -3
  18. package/dist/commonjs/features/pmProgress/components/list/PmDomainList.js.map +1 -1
  19. package/dist/commonjs/features/pmProgress/components/list/{ListItem.js → StandardItem.js} +5 -5
  20. package/dist/commonjs/features/pmProgress/components/list/StandardItem.js.map +1 -0
  21. package/dist/commonjs/features/pmProgress/components/recs/PmTeacherRecs.js +7 -4
  22. package/dist/commonjs/features/pmProgress/components/recs/PmTeacherRecs.js.map +1 -1
  23. package/dist/commonjs/features/pmProgress/components/standard/StandardTag.js +20 -21
  24. package/dist/commonjs/features/pmProgress/components/standard/StandardTag.js.map +1 -1
  25. package/dist/commonjs/features/pmProgress/model/PmInsightsModel.js +3 -2
  26. package/dist/commonjs/features/pmProgress/model/PmInsightsModel.js.map +1 -1
  27. package/dist/commonjs/features/pmProgress/model/PmProgressModel.js +2 -0
  28. package/dist/commonjs/features/pmProgress/model/PmProgressModel.js.map +1 -1
  29. package/dist/commonjs/features/pmProgress/model/PmRecommendationsModel.js +4 -3
  30. package/dist/commonjs/features/pmProgress/model/PmRecommendationsModel.js.map +1 -1
  31. package/dist/commonjs/features/pmProgress/model/PmTreeModel.js +39 -20
  32. package/dist/commonjs/features/pmProgress/model/PmTreeModel.js.map +1 -1
  33. package/dist/commonjs/features/pmProgress/shared/pmProgress.helpers.js +1 -1
  34. package/dist/commonjs/features/pmProgress/shared/pmProgress.helpers.js.map +1 -1
  35. package/dist/commonjs/shared/icons/keyboard/GroszIcon.js +34 -0
  36. package/dist/commonjs/shared/icons/keyboard/GroszIcon.js.map +1 -0
  37. package/dist/commonjs/shared/icons/keyboard/ZlotyIcon.js +34 -0
  38. package/dist/commonjs/shared/icons/keyboard/ZlotyIcon.js.map +1 -0
  39. package/dist/module/features/keyboard/constants/groups/currency.constants.js +3 -0
  40. package/dist/module/features/keyboard/constants/groups/currency.constants.js.map +1 -1
  41. package/dist/module/features/keyboard/constants/keyboard.helpers.js +2 -1
  42. package/dist/module/features/keyboard/constants/keyboard.helpers.js.map +1 -1
  43. package/dist/module/features/keyboard/constants/keys/units.constants.js +16 -0
  44. package/dist/module/features/keyboard/constants/keys/units.constants.js.map +1 -1
  45. package/dist/module/features/pmProgress/components/chart/chart.helpers.js +1 -0
  46. package/dist/module/features/pmProgress/components/chart/chart.helpers.js.map +1 -1
  47. package/dist/module/features/pmProgress/components/header/PmProgressHeader.js +21 -18
  48. package/dist/module/features/pmProgress/components/header/PmProgressHeader.js.map +1 -1
  49. package/dist/module/features/pmProgress/components/header/StartButton.js +10 -1
  50. package/dist/module/features/pmProgress/components/header/StartButton.js.map +1 -1
  51. package/dist/module/features/pmProgress/components/list/{ListCard.js → DomainCard.js} +9 -25
  52. package/dist/module/features/pmProgress/components/list/DomainCard.js.map +1 -0
  53. package/dist/module/features/pmProgress/components/list/Fluency.js +3 -3
  54. package/dist/module/features/pmProgress/components/list/Fluency.js.map +1 -1
  55. package/dist/module/features/pmProgress/components/list/PmDomainList.js +3 -3
  56. package/dist/module/features/pmProgress/components/list/PmDomainList.js.map +1 -1
  57. package/dist/module/features/pmProgress/components/list/{ListItem.js → StandardItem.js} +3 -3
  58. package/dist/module/features/pmProgress/components/list/StandardItem.js.map +1 -0
  59. package/dist/module/features/pmProgress/components/recs/PmTeacherRecs.js +7 -4
  60. package/dist/module/features/pmProgress/components/recs/PmTeacherRecs.js.map +1 -1
  61. package/dist/module/features/pmProgress/components/standard/StandardTag.js +21 -22
  62. package/dist/module/features/pmProgress/components/standard/StandardTag.js.map +1 -1
  63. package/dist/module/features/pmProgress/model/PmInsightsModel.js +3 -2
  64. package/dist/module/features/pmProgress/model/PmInsightsModel.js.map +1 -1
  65. package/dist/module/features/pmProgress/model/PmProgressModel.js +2 -0
  66. package/dist/module/features/pmProgress/model/PmProgressModel.js.map +1 -1
  67. package/dist/module/features/pmProgress/model/PmRecommendationsModel.js +5 -4
  68. package/dist/module/features/pmProgress/model/PmRecommendationsModel.js.map +1 -1
  69. package/dist/module/features/pmProgress/model/PmTreeModel.js +39 -20
  70. package/dist/module/features/pmProgress/model/PmTreeModel.js.map +1 -1
  71. package/dist/module/features/pmProgress/shared/pmProgress.helpers.js +1 -1
  72. package/dist/module/features/pmProgress/shared/pmProgress.helpers.js.map +1 -1
  73. package/dist/module/shared/icons/keyboard/GroszIcon.js +26 -0
  74. package/dist/module/shared/icons/keyboard/GroszIcon.js.map +1 -0
  75. package/dist/module/shared/icons/keyboard/ZlotyIcon.js +26 -0
  76. package/dist/module/shared/icons/keyboard/ZlotyIcon.js.map +1 -0
  77. package/dist/typescript/commonjs/features/keyboard/constants/groups/currency.constants.d.ts.map +1 -1
  78. package/dist/typescript/commonjs/features/keyboard/constants/keyboard.helpers.d.ts.map +1 -1
  79. package/dist/typescript/commonjs/features/keyboard/constants/keys/units.constants.d.ts.map +1 -1
  80. package/dist/typescript/commonjs/features/pmProgress/components/chart/chart.helpers.d.ts.map +1 -1
  81. package/dist/typescript/commonjs/features/pmProgress/components/header/PmProgressHeader.d.ts.map +1 -1
  82. package/dist/typescript/commonjs/features/pmProgress/components/header/StartButton.d.ts.map +1 -1
  83. package/dist/typescript/commonjs/features/pmProgress/components/list/{ListCard.d.ts → DomainCard.d.ts} +6 -6
  84. package/dist/typescript/commonjs/features/pmProgress/components/list/DomainCard.d.ts.map +1 -0
  85. package/dist/typescript/commonjs/features/pmProgress/components/list/StandardItem.d.ts +13 -0
  86. package/dist/typescript/commonjs/features/pmProgress/components/list/StandardItem.d.ts.map +1 -0
  87. package/dist/typescript/commonjs/features/pmProgress/components/recs/PmTeacherRecs.d.ts.map +1 -1
  88. package/dist/typescript/commonjs/features/pmProgress/components/standard/StandardTag.d.ts +4 -2
  89. package/dist/typescript/commonjs/features/pmProgress/components/standard/StandardTag.d.ts.map +1 -1
  90. package/dist/typescript/commonjs/features/pmProgress/model/PmInsightsModel.d.ts +1 -0
  91. package/dist/typescript/commonjs/features/pmProgress/model/PmInsightsModel.d.ts.map +1 -1
  92. package/dist/typescript/commonjs/features/pmProgress/model/PmProgressModel.d.ts.map +1 -1
  93. package/dist/typescript/commonjs/features/pmProgress/model/PmRecommendationsModel.d.ts +1 -0
  94. package/dist/typescript/commonjs/features/pmProgress/model/PmRecommendationsModel.d.ts.map +1 -1
  95. package/dist/typescript/commonjs/features/pmProgress/model/PmTreeModel.d.ts +3 -2
  96. package/dist/typescript/commonjs/features/pmProgress/model/PmTreeModel.d.ts.map +1 -1
  97. package/dist/typescript/commonjs/features/pmProgress/shared/pmProgress.helpers.d.ts.map +1 -1
  98. package/dist/typescript/commonjs/features/pmProgress/shared/pmProgress.types.d.ts +4 -0
  99. package/dist/typescript/commonjs/features/pmProgress/shared/pmProgress.types.d.ts.map +1 -1
  100. package/dist/typescript/commonjs/shared/icons/keyboard/GroszIcon.d.ts +4 -0
  101. package/dist/typescript/commonjs/shared/icons/keyboard/GroszIcon.d.ts.map +1 -0
  102. package/dist/typescript/commonjs/shared/icons/keyboard/ZlotyIcon.d.ts +4 -0
  103. package/dist/typescript/commonjs/shared/icons/keyboard/ZlotyIcon.d.ts.map +1 -0
  104. package/dist/typescript/module/features/keyboard/constants/groups/currency.constants.d.ts.map +1 -1
  105. package/dist/typescript/module/features/keyboard/constants/keyboard.helpers.d.ts.map +1 -1
  106. package/dist/typescript/module/features/keyboard/constants/keys/units.constants.d.ts.map +1 -1
  107. package/dist/typescript/module/features/pmProgress/components/chart/chart.helpers.d.ts.map +1 -1
  108. package/dist/typescript/module/features/pmProgress/components/header/PmProgressHeader.d.ts.map +1 -1
  109. package/dist/typescript/module/features/pmProgress/components/header/StartButton.d.ts.map +1 -1
  110. package/dist/typescript/module/features/pmProgress/components/list/{ListCard.d.ts → DomainCard.d.ts} +6 -6
  111. package/dist/typescript/module/features/pmProgress/components/list/DomainCard.d.ts.map +1 -0
  112. package/dist/typescript/module/features/pmProgress/components/list/StandardItem.d.ts +13 -0
  113. package/dist/typescript/module/features/pmProgress/components/list/StandardItem.d.ts.map +1 -0
  114. package/dist/typescript/module/features/pmProgress/components/recs/PmTeacherRecs.d.ts.map +1 -1
  115. package/dist/typescript/module/features/pmProgress/components/standard/StandardTag.d.ts +4 -2
  116. package/dist/typescript/module/features/pmProgress/components/standard/StandardTag.d.ts.map +1 -1
  117. package/dist/typescript/module/features/pmProgress/model/PmInsightsModel.d.ts +1 -0
  118. package/dist/typescript/module/features/pmProgress/model/PmInsightsModel.d.ts.map +1 -1
  119. package/dist/typescript/module/features/pmProgress/model/PmProgressModel.d.ts.map +1 -1
  120. package/dist/typescript/module/features/pmProgress/model/PmRecommendationsModel.d.ts +1 -0
  121. package/dist/typescript/module/features/pmProgress/model/PmRecommendationsModel.d.ts.map +1 -1
  122. package/dist/typescript/module/features/pmProgress/model/PmTreeModel.d.ts +3 -2
  123. package/dist/typescript/module/features/pmProgress/model/PmTreeModel.d.ts.map +1 -1
  124. package/dist/typescript/module/features/pmProgress/shared/pmProgress.helpers.d.ts.map +1 -1
  125. package/dist/typescript/module/features/pmProgress/shared/pmProgress.types.d.ts +4 -0
  126. package/dist/typescript/module/features/pmProgress/shared/pmProgress.types.d.ts.map +1 -1
  127. package/dist/typescript/module/shared/icons/keyboard/GroszIcon.d.ts +4 -0
  128. package/dist/typescript/module/shared/icons/keyboard/GroszIcon.d.ts.map +1 -0
  129. package/dist/typescript/module/shared/icons/keyboard/ZlotyIcon.d.ts +4 -0
  130. package/dist/typescript/module/shared/icons/keyboard/ZlotyIcon.d.ts.map +1 -0
  131. package/package.json +3 -3
  132. package/src/features/keyboard/constants/groups/currency.constants.ts +3 -0
  133. package/src/features/keyboard/constants/keyboard.helpers.ts +1 -0
  134. package/src/features/keyboard/constants/keys/units.constants.tsx +12 -0
  135. package/src/features/pmProgress/components/chart/chart.helpers.ts +1 -0
  136. package/src/features/pmProgress/components/header/PmProgressHeader.tsx +23 -14
  137. package/src/features/pmProgress/components/header/StartButton.tsx +11 -1
  138. package/src/features/pmProgress/components/list/{ListCard.tsx → DomainCard.tsx} +12 -24
  139. package/src/features/pmProgress/components/list/Fluency.tsx +3 -3
  140. package/src/features/pmProgress/components/list/PmDomainList.tsx +3 -3
  141. package/src/features/pmProgress/components/list/{ListItem.tsx → StandardItem.tsx} +4 -4
  142. package/src/features/pmProgress/components/recs/PmTeacherRecs.tsx +8 -4
  143. package/src/features/pmProgress/components/standard/StandardTag.tsx +29 -23
  144. package/src/features/pmProgress/model/PmInsightsModel.ts +5 -4
  145. package/src/features/pmProgress/model/PmProgressModel.ts +2 -0
  146. package/src/features/pmProgress/model/PmRecommendationsModel.ts +6 -4
  147. package/src/features/pmProgress/model/PmTreeModel.ts +33 -19
  148. package/src/features/pmProgress/shared/pmProgress.helpers.ts +1 -2
  149. package/src/features/pmProgress/shared/pmProgress.types.ts +3 -0
  150. package/src/shared/icons/keyboard/GroszIcon.tsx +20 -0
  151. package/src/shared/icons/keyboard/ZlotyIcon.tsx +20 -0
  152. package/dist/commonjs/features/pmProgress/components/list/ListCard.js.map +0 -1
  153. package/dist/commonjs/features/pmProgress/components/list/ListItem.js.map +0 -1
  154. package/dist/module/features/pmProgress/components/list/ListCard.js.map +0 -1
  155. package/dist/module/features/pmProgress/components/list/ListItem.js.map +0 -1
  156. package/dist/typescript/commonjs/features/pmProgress/components/list/ListCard.d.ts.map +0 -1
  157. package/dist/typescript/commonjs/features/pmProgress/components/list/ListItem.d.ts +0 -13
  158. package/dist/typescript/commonjs/features/pmProgress/components/list/ListItem.d.ts.map +0 -1
  159. package/dist/typescript/module/features/pmProgress/components/list/ListCard.d.ts.map +0 -1
  160. package/dist/typescript/module/features/pmProgress/components/list/ListItem.d.ts +0 -13
  161. package/dist/typescript/module/features/pmProgress/components/list/ListItem.d.ts.map +0 -1
@@ -8,6 +8,8 @@ import { PoundIcon } from '../../../../shared/icons/keyboard/PoundIcon'
8
8
  import { YenIcon } from '../../../../shared/icons/keyboard/YenIcon'
9
9
  import { PesoIcon } from '../../../../shared/icons/keyboard/PesoIcon'
10
10
  import { CentIcon } from '../../../../shared/icons/keyboard/CentIcon'
11
+ import { ZlotyIcon } from '../../../../shared/icons/keyboard/ZlotyIcon'
12
+ import { GroszIcon } from '../../../../shared/icons/keyboard/GroszIcon'
11
13
 
12
14
  export const MATH_CURRENCY_UNITS: MathOperatorsGroup = {
13
15
  KRONE: {
@@ -46,6 +48,16 @@ export const MATH_CURRENCY_UNITS: MathOperatorsGroup = {
46
48
  mathliveValue: '¢',
47
49
  katexValue: '¢',
48
50
  },
51
+ ZLOTY: {
52
+ icon: <ZlotyIcon {...DEFAULT_KEY_ICON_PROPS} />,
53
+ mathliveValue: '\\mathrm{zł}',
54
+ katexValue: 'zł',
55
+ },
56
+ GROSZ: {
57
+ icon: <GroszIcon {...DEFAULT_KEY_ICON_PROPS} />,
58
+ mathliveValue: '\\mathrm{gr}',
59
+ katexValue: 'gr',
60
+ },
49
61
  } as const
50
62
 
51
63
  export const MATH_LENGTH_UNITS: MathOperatorsGroup = {
@@ -1,6 +1,7 @@
1
1
  export const getSliceClipId = (index: number | string) => `slice-clip-${index}`
2
2
 
3
3
  export const dynamicInnerRadius = (sliceCount: number): number => {
4
+ if (sliceCount >= 8) return 20
4
5
  if (sliceCount >= 5) return 14
5
6
  if (sliceCount === 4) return 8
6
7
  if (sliceCount === 3) return 6
@@ -1,6 +1,7 @@
1
1
  import React, { useState } from 'react'
2
- import { Platform, StyleSheet, TextStyle, useWindowDimensions, View } from 'react-native'
2
+ import { ScrollView, StyleSheet, TextStyle, useWindowDimensions, View } from 'react-native'
3
3
  import {
4
+ BORDER_RADIUS,
4
5
  Button,
5
6
  ButtonVariant,
6
7
  CaretDownIcon,
@@ -10,6 +11,7 @@ import {
10
11
  HeadingVariants,
11
12
  Menu,
12
13
  MenuItemProps,
14
+ SHADOWS,
13
15
  SPACING,
14
16
  Typography,
15
17
  } from '@magmamath/react-native-ui'
@@ -50,6 +52,11 @@ export const PmProgressHeader = () => {
50
52
  })),
51
53
  })
52
54
 
55
+ const selectGrade = (key: number) => {
56
+ model.tree.setGrade(key)
57
+ setIsDropdownOpen(false)
58
+ }
59
+
53
60
  return (
54
61
  <View style={styles.container}>
55
62
  <View style={styles.title}>
@@ -91,14 +98,13 @@ export const PmProgressHeader = () => {
91
98
  </Button>
92
99
  }
93
100
  >
94
- <Menu
95
- style={styles.menu}
96
- items={grades}
97
- onItemPress={(item) => {
98
- model.tree.setGrade(item.key as number)
99
- setIsDropdownOpen(false)
100
- }}
101
- />
101
+ <ScrollView style={styles.menuScroll}>
102
+ <Menu
103
+ style={styles.menu}
104
+ items={grades}
105
+ onItemPress={(item) => selectGrade(item.key as number)}
106
+ />
107
+ </ScrollView>
102
108
  </Dropdown>
103
109
  </View>
104
110
 
@@ -129,11 +135,14 @@ const styles = StyleSheet.create({
129
135
  caret: {
130
136
  marginLeft: SPACING[100],
131
137
  },
132
- menu: {
138
+ menuScroll: {
133
139
  top: SPACING[100],
134
- minWidth: 110,
135
- overflow: 'scroll',
136
- maxHeight: MENU_MAX_HEIGHT,
137
- ...Platform.select({ web: { scrollbarWidth: 'none' } }),
140
+ maxHeight: isWeb ? MENU_MAX_HEIGHT : 490,
141
+ borderRadius: BORDER_RADIUS['300'],
142
+ overflow: 'hidden',
143
+ ...SHADOWS[1],
144
+ },
145
+ menu: {
146
+ minWidth: 130,
138
147
  },
139
148
  })
@@ -66,8 +66,18 @@ export const StartButton = ({
66
66
  useNativeDriver: true,
67
67
  })
68
68
 
69
+ const resetAnimations = Animated.parallel([
70
+ Animated.timing(scaleAnimValue, { toValue: 0, duration: 0, useNativeDriver: true }),
71
+ Animated.timing(fadeAnimValue, { toValue: 1, duration: 0, useNativeDriver: true }),
72
+ ])
73
+
69
74
  const pulsation = Animated.loop(
70
- Animated.sequence([scaleAnimation, Animated.delay(PULSE_DELAY), fadeOutAnimation]),
75
+ Animated.sequence([
76
+ resetAnimations,
77
+ scaleAnimation,
78
+ Animated.delay(PULSE_DELAY),
79
+ fadeOutAnimation,
80
+ ]),
71
81
  )
72
82
 
73
83
  pulsation.start()
@@ -9,14 +9,14 @@ import {
9
9
  SPACING,
10
10
  Typography,
11
11
  } from '@magmamath/react-native-ui'
12
- import { ListItem } from './ListItem'
12
+ import { StandardItem } from './StandardItem'
13
13
  import { useText } from '../../../../i18n/i18n'
14
14
 
15
15
  const HEADER_HEIGHT = 60
16
16
  const PROGRESS_BAR_WIDTH = 50
17
17
  const PROGRESS_BAR_HEIGHT = 10
18
18
 
19
- export type ListCardItem = {
19
+ export type DomainCardItem = {
20
20
  id: string
21
21
  title: string
22
22
  subtitle: string
@@ -25,17 +25,11 @@ export type ListCardItem = {
25
25
  testID?: string
26
26
  }
27
27
 
28
- function getVisibleItems<T>(items: T[], maxItems?: number) {
29
- const visible = maxItems != null ? items.slice(0, maxItems) : items
30
- const remainingCount = maxItems != null ? Math.max(0, items.length - maxItems) : 0
31
- return { visible, remainingCount }
32
- }
33
-
34
- type ListCardProps<T extends ListCardItem> = {
28
+ type DomainCardProps<T extends DomainCardItem> = {
35
29
  title: string
36
30
  icon: React.ReactElement
37
31
  color: string
38
- items: T[]
32
+ standards: T[]
39
33
  onItemPress?: (item: T) => void
40
34
  isExpanded?: boolean
41
35
  onPress?: () => void
@@ -45,19 +39,19 @@ type ListCardProps<T extends ListCardItem> = {
45
39
  rightContent?: React.ReactNode
46
40
  isFirst?: boolean
47
41
  isLast?: boolean
48
- maxItems?: number
49
42
  expandedColor?: string
50
43
  isSkillsLeftHidden?: boolean
51
44
  isHighlighted?: boolean
52
45
  loadingItemId?: string | null
46
+ remainingItemsCount?: number
53
47
  testID?: string
54
48
  }
55
49
 
56
- export const ListCard = <T extends ListCardItem>({
50
+ export const DomainCard = <T extends DomainCardItem>({
57
51
  title,
58
52
  icon,
59
53
  color,
60
- items,
54
+ standards,
61
55
  isExpanded = false,
62
56
  isHighlighted = false,
63
57
  onPress,
@@ -68,16 +62,15 @@ export const ListCard = <T extends ListCardItem>({
68
62
  rightContent,
69
63
  isFirst,
70
64
  isLast,
71
- maxItems,
72
65
  expandedColor = COLORS.NEUTRAL_3,
73
66
  isSkillsLeftHidden = false,
74
67
  loadingItemId = null,
68
+ remainingItemsCount = 0,
75
69
  testID,
76
- }: ListCardProps<T>) => {
70
+ }: DomainCardProps<T>) => {
77
71
  const t = useText()
78
72
  const [isHovered, setIsHovered] = useState(false)
79
73
 
80
- const { visible: standards, remainingCount } = getVisibleItems(items, maxItems)
81
74
  const withHighlight = isHovered || isExpanded || isHighlighted
82
75
 
83
76
  const Caret = isExpanded ? CaretDownIcon : CaretRightIcon
@@ -87,7 +80,6 @@ export const ListCard = <T extends ListCardItem>({
87
80
  style={[
88
81
  styles.container,
89
82
  !isFirst && styles.containerCollapsed,
90
- isFirst && styles.containerFirst,
91
83
  isLast && styles.containerLast,
92
84
  withHighlight && { backgroundColor: expandedColor },
93
85
  ]}
@@ -134,7 +126,7 @@ export const ListCard = <T extends ListCardItem>({
134
126
  {isExpanded && (
135
127
  <View style={[styles.items, isLast && styles.itemsLast]}>
136
128
  {standards.map((standard) => (
137
- <ListItem
129
+ <StandardItem
138
130
  key={standard.id}
139
131
  title={standard.title}
140
132
  skillsLeft={standard.skillsLeft}
@@ -146,9 +138,9 @@ export const ListCard = <T extends ListCardItem>({
146
138
  onPress={() => onItemPress?.(standard)}
147
139
  />
148
140
  ))}
149
- {remainingCount > 0 && (
141
+ {remainingItemsCount > 0 && (
150
142
  <Typography variant={HeadingVariants.H8} style={styles.moreText}>
151
- {t('pmProgress.andMore', { count: `${remainingCount}` })}
143
+ {t('pmProgress.andMore', { count: `${remainingItemsCount}` })}
152
144
  </Typography>
153
145
  )}
154
146
  </View>
@@ -167,10 +159,6 @@ const styles = StyleSheet.create({
167
159
  containerCollapsed: {
168
160
  marginTop: -1,
169
161
  },
170
- containerFirst: {
171
- borderTopLeftRadius: BORDER_RADIUS[200],
172
- borderTopRightRadius: BORDER_RADIUS[200],
173
- },
174
162
  containerLast: {
175
163
  borderBottomLeftRadius: BORDER_RADIUS[200],
176
164
  borderBottomRightRadius: BORDER_RADIUS[200],
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
  import { StyleSheet, View } from 'react-native'
3
- import { ListCard } from './ListCard'
3
+ import { DomainCard } from './DomainCard'
4
4
  import { COLORS, SPACING } from '@magmamath/react-native-ui'
5
5
  import { SliceDiamondIcon } from '../../../../shared/icons/SliceDiamondIcon'
6
6
  import { useUnit } from 'effector-react'
@@ -28,9 +28,9 @@ export const Fluency = ({ data }: FluencyProps) => {
28
28
  return (
29
29
  <View style={styles.container}>
30
30
  <View style={styles.separator} />
31
- <ListCard
31
+ <DomainCard
32
32
  title={t('pmProgress.fluency')}
33
- items={data.standards.map((standard) => ({ ...standard, id: standard.standardId }))}
33
+ standards={data.standards.map((standard) => ({ ...standard, id: standard.standardId }))}
34
34
  progress={data.scorePercentage}
35
35
  color={COLORS.PRIMARY_ORANGE}
36
36
  onPress={() => model.highlight.expand(data.domainId)}
@@ -2,7 +2,7 @@ import React from 'react'
2
2
  import { StyleSheet, View } from 'react-native'
3
3
  import { useUnit } from 'effector-react'
4
4
  import { BORDER_RADIUS, COLORS, ScrollableList, SHADOWS, SPACING } from '@magmamath/react-native-ui'
5
- import { ListCard } from './ListCard'
5
+ import { DomainCard } from './DomainCard'
6
6
  import { Fluency } from './Fluency'
7
7
  import { SliceDiamondIcon } from '../../../../shared/icons/SliceDiamondIcon'
8
8
  import { usePmProgress } from '../../context/PmProgressContext'
@@ -30,10 +30,10 @@ export const PmDomainList = () => {
30
30
  ListFooterComponent={<Fluency data={fluency} />}
31
31
  keyExtractor={(item) => item.domainId}
32
32
  renderItem={({ item, index }) => (
33
- <ListCard
33
+ <DomainCard
34
34
  title={item.title}
35
35
  testID={TEST_IDS.PM_PROGRESS_DOMAIN_CARD(item.domainId)}
36
- items={item.standards.map((standard) => ({
36
+ standards={item.standards.map((standard) => ({
37
37
  ...standard,
38
38
  id: standard.standardId,
39
39
  testID: TEST_IDS.PM_PROGRESS_STANDARD_ITEM(standard.standardId),
@@ -18,7 +18,7 @@ const ICON_SIZE = 25
18
18
  const SUBTITLE_FONT_SIZE = 10
19
19
  const SUBTITLE_LINE_HEIGHT = 14
20
20
 
21
- export type ListItemProps = {
21
+ export type StandardItemProps = {
22
22
  title: string
23
23
  subtitle: string
24
24
  skillsLeft: number
@@ -29,7 +29,7 @@ export type ListItemProps = {
29
29
  testID?: string
30
30
  }
31
31
 
32
- export const ListItem = ({
32
+ export const StandardItem = ({
33
33
  title,
34
34
  subtitle,
35
35
  skillsLeft,
@@ -38,11 +38,11 @@ export const ListItem = ({
38
38
  onPress,
39
39
  isSkillsLeftHidden = false,
40
40
  testID,
41
- }: ListItemProps) => {
41
+ }: StandardItemProps) => {
42
42
  const t = useText()
43
43
  const [isHovered, setIsHovered] = useState(false)
44
44
  const isSkillsLeftVisible = skillsLeft > 0 && !isFinished && !isSkillsLeftHidden
45
- const hasDistinctSubtitle = title !== subtitle
45
+ const hasDistinctSubtitle = subtitle && title !== subtitle
46
46
 
47
47
  return (
48
48
  <Pressable
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
  import { StyleSheet, Text, View } from 'react-native'
3
- import { ListCard } from '../list/ListCard'
3
+ import { DomainCard } from '../list/DomainCard'
4
4
  import { PentagonIcon } from '../../../../shared/icons/PentagonIcon'
5
5
  import {
6
6
  BORDER_RADIUS,
@@ -39,15 +39,19 @@ export const PmTeacherRecs = () => {
39
39
  }),
40
40
  )
41
41
 
42
+ const maxVisible = 3
43
+ const visibleItems = recommendations.slice(0, maxVisible)
44
+ const remainingItemsCount = Math.max(0, total - maxVisible)
45
+
42
46
  if (!recommendations.length) return null
43
47
 
44
48
  return (
45
49
  <View style={styles.container}>
46
50
  <View style={styles.wrapper}>
47
- <ListCard
51
+ <DomainCard
48
52
  title={t('pmProgress.teacherRecs')}
49
- items={recommendations}
50
- maxItems={3}
53
+ standards={visibleItems}
54
+ remainingItemsCount={remainingItemsCount}
51
55
  color={COLORS.PRIMARY_YELLOW}
52
56
  expandedColor={COLORS.YELLOW_1}
53
57
  testID={TEST_IDS.PM_PROGRESS_TEACHER_RECS_CARD}
@@ -1,10 +1,11 @@
1
1
  import React from 'react'
2
- import { StyleSheet, View, Text } from 'react-native'
2
+ import { StyleSheet, View, Text, StyleProp, ViewStyle } from 'react-native'
3
3
  import { PmProgressModel } from '@magmamath/students-features'
4
4
  import { SliceDiamondIcon } from '../../../../shared/icons/SliceDiamondIcon'
5
- import { COLORS, FONT_FAMILY } from '@magmamath/react-native-ui'
5
+ import { BORDER_RADIUS, COLORS, FONT_FAMILY, SHADOWS, SPACING } from '@magmamath/react-native-ui'
6
6
  import { useStoreMap } from 'effector-react'
7
7
  import { isWeb } from '../../../../shared/common.constants'
8
+ import { findDomainBySkillId } from '../../shared/pmProgress.helpers'
8
9
 
9
10
  type ColorPreset = {
10
11
  background: string
@@ -113,36 +114,45 @@ const DOMAIN_COLOR_PRESETS: Record<string, ColorPreset> = {
113
114
  }
114
115
 
115
116
  type StandardTagProps = {
116
- skillId: string
117
117
  model: PmProgressModel
118
+ skillId: string
119
+ style?: StyleProp<ViewStyle>
118
120
  }
119
121
 
120
- export const StandardTag = ({ skillId, model }: StandardTagProps) => {
122
+ export const StandardTag = ({ model, skillId, style }: StandardTagProps) => {
121
123
  const standardInfo = useStoreMap({
122
- store: model.tree.$domainsInGrade,
124
+ store: model.tree.$domains,
123
125
  keys: [skillId],
124
- fn: (domains, [skillId]) => {
125
- const domain = domains.find((d) => d.standards.some((s) => s.skillIds.includes(skillId)))
126
- const standard = domain?.standards.find(({ skillIds }) => skillIds.includes(skillId))
127
- if (!standard || !domain) return null
126
+ fn: (domainsByGrade, [skillId]) => {
127
+ const allDomains = Object.values(domainsByGrade).flat()
128
+ const domain = findDomainBySkillId(skillId, allDomains)
129
+ if (!domain) return null
130
+
131
+ const standard = domain.standards.find(({ skillIds }) => skillIds.includes(skillId))
132
+ if (!standard) return null
128
133
  return { title: standard.title, tag: standard.subtitle, color: domain.color }
129
134
  },
130
135
  })
131
136
 
132
137
  if (!standardInfo) return null
133
138
 
139
+ const hasStandardTag = !!standardInfo.tag
140
+ const isTagDifferentFromTitle = standardInfo.title !== standardInfo.tag
141
+
134
142
  const preset = DOMAIN_COLOR_PRESETS[standardInfo.color]
135
143
 
136
144
  return (
137
- <View style={[styles.container, { backgroundColor: preset.background }]}>
145
+ <View style={[styles.container, { backgroundColor: preset.background }, style]}>
138
146
  <SliceDiamondIcon color={preset.tagColor} />
139
147
  <View style={styles.textContainer}>
140
148
  <Text numberOfLines={2} style={[styles.title, { color: preset.titleColor }]}>
141
149
  {standardInfo.title}
142
150
  </Text>
143
- <Text numberOfLines={1} style={[styles.tag, { color: preset.tagColor }]}>
144
- {standardInfo.tag}
145
- </Text>
151
+ {hasStandardTag && isTagDifferentFromTitle && (
152
+ <Text numberOfLines={1} style={[styles.tag, { color: preset.tagColor }]}>
153
+ {standardInfo.tag}
154
+ </Text>
155
+ )}
146
156
  </View>
147
157
  </View>
148
158
  )
@@ -150,20 +160,16 @@ export const StandardTag = ({ skillId, model }: StandardTagProps) => {
150
160
 
151
161
  const styles = StyleSheet.create({
152
162
  container: {
153
- minWidth: 110,
163
+ minWidth: 120,
154
164
  maxWidth: isWeb ? 350 : 280,
155
- paddingHorizontal: 8,
156
- paddingVertical: 4,
157
- borderRadius: 12,
165
+ paddingHorizontal: SPACING[200],
166
+ paddingVertical: SPACING[100],
167
+ borderRadius: BORDER_RADIUS[300],
158
168
  flexDirection: 'row',
159
169
  alignItems: 'center',
160
- gap: 8,
170
+ gap: SPACING[200],
161
171
  alignSelf: 'flex-start',
162
- shadowColor: '#333333',
163
- shadowOffset: { width: 0, height: 1 },
164
- shadowOpacity: 0.1,
165
- shadowRadius: 3,
166
- elevation: 2,
172
+ ...SHADOWS[1],
167
173
  },
168
174
  textContainer: {
169
175
  ...(isWeb ? { flex: 1 } : { flexShrink: 1 }),
@@ -3,13 +3,14 @@ import { UserTimeframeStats } from '../shared/pmProgress.types'
3
3
  import { PmProgressApi } from './PmProgressApi'
4
4
  import { InsightsTimeframe } from '../shared/pmProgress.constants'
5
5
 
6
+ type TimeframeStats = UserTimeframeStats['practiceModeStatsByTimeFrame'] | null
7
+
6
8
  export class PmInsightsModel {
9
+ public readonly reset = createEvent()
7
10
  public readonly setTimeframe = createEvent<InsightsTimeframe>()
8
11
 
9
- private readonly $data = createStore<UserTimeframeStats['practiceModeStatsByTimeFrame'] | null>(
10
- null,
11
- )
12
- public readonly $timeframe = restore(this.setTimeframe, InsightsTimeframe.WEEK)
12
+ private readonly $data = createStore<TimeframeStats>(null).reset(this.reset)
13
+ public readonly $timeframe = restore(this.setTimeframe, InsightsTimeframe.WEEK).reset(this.reset)
13
14
  public readonly $insights = combine(this.$data, this.$timeframe, (stats, timeframe) => {
14
15
  return stats ? stats[timeframe] : null
15
16
  })
@@ -1,5 +1,6 @@
1
1
  import { sample } from 'effector'
2
2
  import { createGate } from 'effector-react'
3
+ import { not } from 'patronum/not'
3
4
  import { PmProgressGateProps } from '../shared/pmProgress.types'
4
5
  import { PmProgressApi, type PmProgressApiProps } from './PmProgressApi'
5
6
  import { PmTreeModel } from './PmTreeModel'
@@ -36,6 +37,7 @@ export class PmProgressModel {
36
37
  private init() {
37
38
  sample({
38
39
  clock: this.gate.open,
40
+ filter: not(this.tree.initTreeFx.pending),
39
41
  target: [this.tree.initTreeFx, this.api.getUserInsightsFx],
40
42
  })
41
43
 
@@ -1,13 +1,15 @@
1
1
  import { PmProgressApi } from './PmProgressApi'
2
- import { createEffect, createStore } from 'effector'
2
+ import { createEffect, createEvent, createStore, sample } from 'effector'
3
3
  import { Recommendations, RecsResponse, TreeMappingResponse } from '../shared/pmProgress.types'
4
4
  import { RECOMMENDATIONS_LIMIT_PARAM, SolvingFlow } from '../shared/pmProgress.constants'
5
5
 
6
- const INITIAL_RECOMMENDATION_STATE: Recommendations = { items: [], offset: 0, limit: 0, total: 0 }
6
+ const EMPTY_RECS: Recommendations = { items: [], offset: 0, limit: 0, total: 0 }
7
7
 
8
8
  export class PmRecommendationsModel {
9
- public readonly $teacher = createStore<Recommendations>(INITIAL_RECOMMENDATION_STATE)
10
- public readonly $magma = createStore<Recommendations>(INITIAL_RECOMMENDATION_STATE)
9
+ public readonly reset = createEvent()
10
+
11
+ public readonly $teacher = createStore<Recommendations>(EMPTY_RECS).reset(this.reset)
12
+ public readonly $magma = createStore<Recommendations>(EMPTY_RECS).reset(this.reset)
11
13
 
12
14
  constructor(private readonly api: PmProgressApi) {
13
15
  this.$teacher.on(this.fetchTeacherRecsFx.doneData, (_, data) => data)
@@ -25,22 +25,26 @@ import { persist } from 'effector-storage/session'
25
25
  import { PmProgressStatus, SessionStorageKeys } from '../shared/pmProgress.constants'
26
26
  import { isWeb } from '../../../shared/common.constants'
27
27
 
28
+ type InitTreeFxParams = PmProgressGateProps & { source: { tree: TreeMappingResponse | null } }
29
+
28
30
  export class PmTreeModel {
29
- private readonly $tree = createStore<TreeMappingResponse | null>(null)
30
- private readonly $stats = createStore<StatsResponse | null>(null)
31
- private readonly $user = createStore<User | null>(null)
31
+ public readonly reset = createEvent()
32
+
33
+ public readonly $tree = createStore<TreeMappingResponse | null>(null).reset(this.reset)
34
+ private readonly $user = createStore<User | null>(null).reset(this.reset)
35
+ private readonly $stats = createStore<StatsResponse | null>(null).reset(this.reset)
32
36
 
33
37
  private readonly setupInitialGrade = createEvent<SetupInitialGradeProps>()
34
38
  public readonly setGrade = createEvent<number | null>()
35
39
 
36
- public readonly $isInitialized = createStore(false)
37
- public readonly $grade = restore(this.setGrade, null)
38
- public readonly $domains = createStore<Record<number, Domain[]>>({})
40
+ public readonly $isInitialized = createStore(false).reset(this.reset)
41
+ public readonly $grade = restore(this.setGrade, null).reset(this.reset)
42
+ public readonly $domains = createStore<Record<number, Domain[]>>({}).reset(this.reset)
39
43
  public readonly $domainsInGrade = combine(this.$domains, this.$grade, (domains, grade) =>
40
44
  grade !== null ? (domains[grade] ?? []) : [],
41
45
  )
42
- public readonly $grades = createStore<Grade[]>([])
43
- public readonly $status = createStore<PmProgressStatus | null>(null)
46
+ public readonly $grades = createStore<Grade[]>([]).reset(this.reset)
47
+ public readonly $status = createStore<PmProgressStatus | null>(null).reset(this.reset)
44
48
 
45
49
  constructor(private readonly api: PmProgressApi) {
46
50
  if (isWeb) {
@@ -54,16 +58,26 @@ export class PmTreeModel {
54
58
  this.setupDomainsInTree()
55
59
  }
56
60
 
57
- public readonly initTreeFx = createEffect(async ({ user }: PmProgressGateProps) => {
58
- if (!user.setting.practiceModeTreeId) {
59
- throw new Error('practiceModeTreeId is not set')
60
- }
61
+ public readonly initTreeFx = attach({
62
+ source: { tree: this.$tree },
63
+ mapParams: (params: PmProgressGateProps, source): InitTreeFxParams => ({ ...params, source }),
64
+ effect: createEffect(async ({ user, skipTreeFetch, source }: InitTreeFxParams) => {
65
+ if (!user.setting.practiceModeTreeId) {
66
+ throw new Error('practiceModeTreeId is not set')
67
+ }
61
68
 
62
- const [tree, stats] = await Promise.all([
63
- this.api.getTreeFx(user.setting.practiceModeTreeId),
64
- this.api.getStatisticsFx({ entityIds: [user._id] }),
65
- ])
66
- return [TreeNormalizer.normalize(tree), stats] as const
69
+ if (skipTreeFetch && source.tree) {
70
+ const stats = await this.api.getStatisticsFx({ entityIds: [user._id] })
71
+ return [source.tree, stats] as const
72
+ }
73
+
74
+ const [tree, stats] = await Promise.all([
75
+ this.api.getTreeFx(user.setting.practiceModeTreeId),
76
+ this.api.getStatisticsFx({ entityIds: [user._id] }),
77
+ ])
78
+
79
+ return [TreeNormalizer.normalize(tree), stats] as const
80
+ }),
67
81
  })
68
82
 
69
83
  public readonly getTreeContextFx = attach({
@@ -117,12 +131,12 @@ export class PmTreeModel {
117
131
  $isInitialized: this.$isInitialized,
118
132
  setupInitialGrade: this.setupInitialGrade,
119
133
  },
120
- fn: (target, { params: { user }, result: [tree, stats] }) => {
134
+ fn: (target, { params: { user, skipTreeFetch }, result: [tree, stats] }) => {
121
135
  target.$tree(tree)
122
136
  target.$stats(stats)
123
137
  target.$user(user)
124
138
  target.$grades(getTreeGrades(tree, user))
125
- target.setupInitialGrade({ tree, user })
139
+ if (!skipTreeFetch) target.setupInitialGrade({ tree, user })
126
140
  target.$isInitialized(true)
127
141
  },
128
142
  })
@@ -30,8 +30,7 @@ const groupStandardsByDomain = (
30
30
  }
31
31
 
32
32
  const completed =
33
- !skillsToPractice ||
34
- (skillsToPractice.failed.length === 0 && skillsToPractice.unattempted.length === 0)
33
+ skillsToPractice?.failed.length === 0 && skillsToPractice.unattempted.length === 0
35
34
  const left = skillsToPractice
36
35
  ? skillsToPractice.failed.length + skillsToPractice.unattempted.length
37
36
  : 0
@@ -171,10 +171,13 @@ export type GradeLabelsMap = Record<string, GradeLabel>
171
171
 
172
172
  export type PmProgressGateProps = {
173
173
  user: User
174
+ skipTreeFetch?: boolean
174
175
  }
175
176
 
176
177
  export type SetupInitialGradeProps = { tree: TreeMappingResponse; user: User }
177
178
 
179
+ export type RefetchDataProps = { user?: User }
180
+
178
181
  export type GetTreeContextFxParams = {
179
182
  tree: TreeMappingResponse
180
183
  stats: StatsResponse
@@ -0,0 +1,20 @@
1
+ import React from 'react'
2
+ import { IconProps } from './icon.types'
3
+ import Svg, { Text as SvgText } from 'react-native-svg'
4
+
5
+ export const GroszIcon = ({ size = 20, color }: IconProps) => {
6
+ return (
7
+ <Svg width={size} height={size} viewBox="0 0 16 16" fill="none">
8
+ <SvgText
9
+ x="8"
10
+ y="12"
11
+ fill={color}
12
+ fontSize="10"
13
+ fontWeight="600"
14
+ textAnchor="middle"
15
+ >
16
+ gr
17
+ </SvgText>
18
+ </Svg>
19
+ )
20
+ }
@@ -0,0 +1,20 @@
1
+ import React from 'react'
2
+ import { IconProps } from './icon.types'
3
+ import Svg, { Text as SvgText } from 'react-native-svg'
4
+
5
+ export const ZlotyIcon = ({ size = 20, color }: IconProps) => {
6
+ return (
7
+ <Svg width={size} height={size} viewBox="0 0 16 16" fill="none">
8
+ <SvgText
9
+ x="8"
10
+ y="12"
11
+ fill={color}
12
+ fontSize="10"
13
+ fontWeight="600"
14
+ textAnchor="middle"
15
+ >
16
+
17
+ </SvgText>
18
+ </Svg>
19
+ )
20
+ }