-
Notifications
You must be signed in to change notification settings - Fork 0
/
product.ts
151 lines (126 loc) · 3.99 KB
/
product.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Water sport activity
import { ProductVariant } from "./variants";
import { isEqual } from "lodash";
// "type" of activity
export enum Program {
beginner = "beginner",
kids = "kids",
wave = "wave",
freeride = "freeride",
freestyle = "freestyle",
slalom = "slalom",
race = "race",
foiling = "foiling"
}
export enum ProductType {
board = "board",
propulsion = "propulsion"
}
export type ProductSubType = BoardType | PropulsionType;
// A product can be either a propulsion or a board
export enum PropulsionType {
windsurfSail = "windsurfSail",
kite = "kite",
wing = "wing"
}
export enum BoardType {
windsurfBoard = "windsurfBoard",
surfBoard = "surfBoard",
kiteBoard = "kiteBoard",
paddleBoard = "paddleBoard",
wingBoard = "wingBoard"
}
// This is the actual product a brand is selling
// The VariantType should be a simple type with a few properties. It's the type that makes
// all "variants" of this gear, unique
// Example:
// interface MyVariant {
// size: number;
// construction: string;
// }
export interface Product<VariantType, Type extends ProductVariant<VariantType> = ProductVariant<VariantType>> {
// Type and sub-type
type: ProductType;
subType: ProductSubType;
// Name of the brand
brandName: string;
// A name should be unique for a brand a year and a version
name: string;
// 1st release year. Can remain a few years at the catalogue
year: number;
// Some product have a version that lasts for many years
version?: string;
// URL to get more info about the product
infoUrl?: string;
// Main programs the gear is targeting
programs: Program[];
// Description is per language
description: { [language: string]: string };
// List of pictures that are more or less specific to a variant
pictures: Picture<VariantType>[];
// List of keys that define "variants" of this gear.
// Example: ['size', 'construction'] means you expect variants to have different size and construction values
dimensions: (keyof VariantType)[];
// The actual variants of this gear
// Each variant has a "variant" property, of type VariantType, that defines how this variant is "unique"
variants: Type[];
}
// A picture applies to a particular variant of the gear
// (or to any variant, if variant is empty)
export interface Picture<VariantType> {
variant: Partial<VariantType>;
url: string;
}
/**
* Look for the variant in list that is the closest to the provided variant.
* Could be the exact same variant, or one with "more general" variant
* @param search
* @param listOfVariants
*/
export const getClosestVariant = <VariantType,
P extends { variant: Partial<VariantType> }>(
search: Partial<VariantType>,
listOfVariants: P[]
): P | undefined => {
const searchKeys = Object.keys(search);
if (searchKeys.length === 0) return undefined;
// Try to find an exact match
let find = listOfVariants.find(v => isEqual(v.variant, search));
if (find) return find;
// As a fallback, compare each key: if they are different, it's a no go. Otherwise, keep the variant that has the most common values
return listOfVariants.map(variant => {
let score = 0;
for (let i = 0; i < searchKeys.length; i++) {
const key = searchKeys[i];
if (variant.variant[key] !== undefined) {
if (variant.variant[key] === search[key]) {
// The variant is equal on this key => increase score
score++;
} else {
// The variant has a different value => score is zero
score = 0;
break;
}
}
// Otherwise if the variant doesn't have this key, don't change the score
}
return {
variant,
score
};
})
// Keep only variants that don't have a different value
.filter(v => v.score !== 0)
// Take the highest match score if it exists
.sort((vA, vB) => vA.score - vB.score)
[0]?.variant;
};
export interface WithSize {
size: string;
}
export interface WithEdition {
edition: string;
}
export interface WithConstruction {
construction: string;
}