github.com/shoshinnikita/budget-manager@v0.7.1-0.20220131195411-8c46ff1c6778/internal/web/pages/spend_type.go (about) 1 package pages 2 3 import ( 4 "fmt" 5 "sort" 6 7 "github.com/ShoshinNikita/budget-manager/internal/db" 8 ) 9 10 type SpendType struct { 11 db.SpendType 12 13 // FullName is a composite name that contains names of parent Spend Types 14 FullName string 15 // parentSpendTypeIDs is a set of ids of parent Spend Types 16 parentSpendTypeIDs map[uint]struct{} 17 } 18 19 // getSpendTypesWithFullNames returns sorted Spend Types with full name 20 func getSpendTypesWithFullNames(spendTypes []db.SpendType) []SpendType { 21 spendTypesMap := make(map[uint]db.SpendType, len(spendTypes)) 22 for _, t := range spendTypes { 23 spendTypesMap[t.ID] = t 24 } 25 26 res := make([]SpendType, 0, len(spendTypes)) 27 for _, t := range spendTypes { 28 fullName, parentIDs := getSpendTypeFullName(spendTypesMap, t.ID) 29 res = append(res, SpendType{ 30 SpendType: t, 31 // 32 FullName: fullName, 33 parentSpendTypeIDs: parentIDs, 34 }) 35 } 36 37 sort.Slice(res, func(i, j int) bool { 38 return res[i].FullName < res[j].FullName 39 }) 40 41 return res 42 } 43 44 func getSpendTypeFullName(spendTypes map[uint]db.SpendType, typeID uint) (name string, parentIDs map[uint]struct{}) { 45 const maxDepth = 15 46 47 parentIDs = make(map[uint]struct{}) 48 49 var getFullName func(currentDepth int, currentType db.SpendType) string 50 getFullName = func(currentDepth int, currentType db.SpendType) string { 51 if currentDepth >= maxDepth { 52 return "..." 53 } 54 if currentType.ParentID == 0 { 55 return currentType.Name 56 } 57 58 nextType := spendTypes[currentType.ParentID] 59 if nextType.Name == "" { 60 return currentType.Name 61 } 62 63 parentIDs[currentType.ParentID] = struct{}{} 64 65 // Use thin spaces 'ā' to separate names 66 return fmt.Sprintf("%sā/ā%s", getFullName(currentDepth+1, nextType), currentType.Name) 67 } 68 69 if spendType, ok := spendTypes[typeID]; ok { 70 return getFullName(0, spendType), parentIDs 71 } 72 return "", nil 73 } 74 75 // populateMonthlyPaymentsWithFullSpendTypeNames replaces Spend Type names to full ones 76 func populateMonthlyPaymentsWithFullSpendTypeNames(spendTypes []SpendType, monthlyPayments []db.MonthlyPayment) { 77 fullNames := make(map[uint]string, len(spendTypes)) 78 for _, t := range spendTypes { 79 fullNames[t.ID] = t.FullName 80 } 81 82 for i := range monthlyPayments { 83 if monthlyPayments[i].Type != nil { 84 if fullName, ok := fullNames[monthlyPayments[i].Type.ID]; ok { 85 monthlyPayments[i].Type.Name = fullName 86 } 87 } 88 } 89 } 90 91 // populateSpendsWithFullSpendTypeNames replaces Spend Type names to full ones 92 func populateSpendsWithFullSpendTypeNames(spendTypes []SpendType, spends []db.Spend) { 93 fullNames := make(map[uint]string, len(spendTypes)) 94 for _, t := range spendTypes { 95 fullNames[t.ID] = t.FullName 96 } 97 98 for i := range spends { 99 if spends[i].Type != nil { 100 if fullName, ok := fullNames[spends[i].Type.ID]; ok { 101 spends[i].Type.Name = fullName 102 } 103 } 104 } 105 }