change product to dump to object before storing in redix. TODO: solve dimensions issue.
This commit is contained in:
144
lib/product.ts
144
lib/product.ts
@ -1,5 +1,6 @@
|
||||
import uuid from "react-native-uuid";
|
||||
import convert, { Area, Length } from "convert";
|
||||
import { Transform } from "class-transformer";
|
||||
|
||||
export type Id = string;
|
||||
|
||||
@ -14,18 +15,6 @@ export type ProductAttributes = {
|
||||
currency?: Currency,
|
||||
// [index:string]: any,
|
||||
}
|
||||
|
||||
export type ProductData = {
|
||||
id?: Id,
|
||||
pricePerUnit: number,
|
||||
measurement: {
|
||||
unit: string,
|
||||
value: number,
|
||||
dimension: number,
|
||||
},
|
||||
attributes?: ProductAttributes,
|
||||
};
|
||||
|
||||
export type length_t = {
|
||||
l: number, u: Length
|
||||
}
|
||||
@ -36,67 +25,76 @@ export type area_t = length_t & {
|
||||
|
||||
export type dimensions_t = area_t | length_t;
|
||||
|
||||
export type ProductData = {
|
||||
id?: Id,
|
||||
pricePerUnit: number,
|
||||
dimensions: dimensions_t,
|
||||
attributes?: ProductAttributes,
|
||||
};
|
||||
|
||||
|
||||
export type product_type_t = "area" | "length";
|
||||
|
||||
export const isArea = (d: dimensions_t) => ("width" in d);
|
||||
export const isLength = (d: dimensions_t) => (!("width" in d));
|
||||
export const dimensionType = (d: dimensions_t) => isArea(d) ? "area" : "length"
|
||||
|
||||
export class Product {
|
||||
public id: string;
|
||||
public area?: area_t;
|
||||
public length?: length_t;
|
||||
public presentUnits: Length;
|
||||
export function matchDimensions(d1: dimensions_t, d2: dimensions_t) {
|
||||
if (!
|
||||
(
|
||||
(isArea(d1) && isArea(d2)) ||
|
||||
(isLength(d1) && isLength(d2))
|
||||
)
|
||||
) {
|
||||
throw new Error(`Dimension mismatch: ${JSON.stringify(d1)} / ${JSON.stringify(d1)}`);
|
||||
}
|
||||
|
||||
constructor(public pricePerUnit: number, dimensions: dimensions_t, public attributes: ProductAttributes = {},) {
|
||||
this.id = attributes.id || uuid.v4().toString();
|
||||
this.presentUnits = dimensions.u;
|
||||
if ("w" in dimensions) {
|
||||
this.area = {
|
||||
l: convert(dimensions.l, dimensions.u).to("meter"),
|
||||
w: convert(dimensions.w, dimensions.u).to("meter"),
|
||||
u: "meter"
|
||||
}
|
||||
} else {
|
||||
this.length = {
|
||||
l: convert(dimensions.l, dimensions.u).to("meter"),
|
||||
u: "meter"
|
||||
};
|
||||
}
|
||||
return {
|
||||
l: convert(d1.l, d1.u).to(d2.u),
|
||||
u: d2.u,
|
||||
...(
|
||||
"w" in d1 ?
|
||||
{ w: convert(d1.w, d1.u).to(d2.u), }
|
||||
: {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export function dimensionArea(d: dimensions_t) {
|
||||
return "w" in d ? d.w * d.l : 0;
|
||||
}
|
||||
|
||||
export class Product {
|
||||
|
||||
public id? : Id;
|
||||
|
||||
constructor(public pricePerUnit: number, public dimensions: dimensions_t, public attributes: ProductAttributes = {},
|
||||
id?: Id,
|
||||
) {
|
||||
this.id = id || uuid.v4().toString();
|
||||
}
|
||||
|
||||
public priceFor(dimensions: dimensions_t): number {
|
||||
if (this.area && "w" in dimensions) {
|
||||
const thisA = this.area.l * this.area.w;
|
||||
const otherA = convert(
|
||||
dimensions.w,
|
||||
dimensions.u
|
||||
).to("meter") * convert(
|
||||
dimensions.l,
|
||||
dimensions.u
|
||||
).to("meter");
|
||||
return (otherA / thisA) * this.pricePerUnit;
|
||||
} if (this.length) {
|
||||
const thisL = this.length.l;
|
||||
const otherL = convert(
|
||||
dimensions.l,
|
||||
dimensions.u
|
||||
).to("meter");
|
||||
return (otherL / thisL) * this.pricePerUnit;
|
||||
}
|
||||
throw new Error(`Invalid dimensions: ${dimensions}`);
|
||||
const dim = matchDimensions(dimensions, this.dimensions);
|
||||
return (
|
||||
dim.w ? dimensionArea(dim) / dimensionArea(this.dimensions) * this.pricePerUnit
|
||||
: (dim.l / this.dimensions.l) * this.pricePerUnit
|
||||
)
|
||||
}
|
||||
|
||||
get priceDisplay() {
|
||||
const u = this.presentUnits;
|
||||
if (this.area) {
|
||||
const w = Math.round(convert(this.area.w, this.area.u).to(this.presentUnits));
|
||||
const l = Math.round(convert(this.area.l, this.area.u).to(this.presentUnits));
|
||||
return `$ ${this.pricePerUnit} / ${l} x ${w} ${u}`;
|
||||
} else if (this.length) {
|
||||
const l = Math.round(convert(this.length.l, this.length.u).to(this.presentUnits));
|
||||
return `$ ${this.pricePerUnit} per ${l} ${u}`;
|
||||
}
|
||||
return this.pricePerUnit.toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})
|
||||
}
|
||||
|
||||
get pricePerUnitDisplay() {
|
||||
const p = this.priceDisplay;
|
||||
const {l, u} = this.dimensions;
|
||||
const w = (this.dimensions as area_t).w || null;
|
||||
const d = w ? `${l}${u} x ${l}${u}` : `${l}${u}`;
|
||||
return `$${p} per ${d}`
|
||||
}
|
||||
|
||||
get attributesAsList() {
|
||||
@ -106,6 +104,30 @@ export class Product {
|
||||
}
|
||||
|
||||
public removeAttribute(key: string) {
|
||||
delete this.attributes[key];
|
||||
this.attributes = Object.fromEntries(
|
||||
Object.entries(this.attributes).filter(
|
||||
([k, v]) => {
|
||||
k == key;
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
get asObject() : ProductData {
|
||||
return {
|
||||
id: this.id,
|
||||
pricePerUnit: this.pricePerUnit,
|
||||
dimensions: this.dimensions,
|
||||
attributes: this.attributes,
|
||||
}
|
||||
}
|
||||
|
||||
static fromObject({id, pricePerUnit, dimensions, attributes} : ProductData) {
|
||||
return new Product(
|
||||
pricePerUnit,
|
||||
dimensions,
|
||||
attributes,
|
||||
id,
|
||||
)
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ import { RenderOptions, render } from "@testing-library/react-native";
|
||||
import { PropsWithChildren, ReactElement } from "react";
|
||||
import { Provider } from "react-redux";
|
||||
import { setupStore, RootState } from "@/app/store";
|
||||
import { Product } from "@/lib/product";
|
||||
import { Product, ProductData } from "@/lib/product";
|
||||
|
||||
export interface ExtendedRenderOptions extends Omit<RenderOptions, 'queries'> {
|
||||
preloadedState?: Partial<RootState>;
|
||||
@ -12,7 +12,7 @@ export interface ExtendedRenderOptions extends Omit<RenderOptions, 'queries'> {
|
||||
export function renderWithProviders(
|
||||
ui: ReactElement,
|
||||
preloadedState = {
|
||||
products: [] as Product []
|
||||
products: [] as ProductData []
|
||||
},
|
||||
extendedRenderOptions: ExtendedRenderOptions = {},
|
||||
) {
|
||||
|
0
lib/util.ts
Normal file
0
lib/util.ts
Normal file
Reference in New Issue
Block a user