add area carpet fixture. Add carpet roll calculator test. refactor carpet roll as own component. add icons.

This commit is contained in:
Jordan 2024-08-10 10:06:25 -07:00
parent dbba262044
commit a463189052
19 changed files with 471 additions and 65 deletions

View File

@ -103,4 +103,11 @@ export default [
type: "lumber",
attributes: { name: "gutter spouts" },
},
{
id: uuid.v4().valueOf(),
pricePerUnit: 0.75,
dimensions: { l: 1, w: 1, u: "ft" },
type: "area_rug",
attributes: { name: "area rug" },
},
] as Array<Product>;

View File

@ -1,11 +1,17 @@
import { Tabs } from 'expo-router';
import { Tabs } from "expo-router";
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
import { TabBarIcon } from '@/components/navigation/TabBarIcon';
import { Provider } from 'react-redux';
import fixtures from "@/__fixtures__/initialProducts"
import { setupStore } from '../store';
import { Colors } from "@/constants/Colors";
import { useColorScheme } from "@/hooks/useColorScheme";
import { TabBarIcon } from "@/components/navigation/TabBarIcon";
import { Provider } from "react-redux";
import fixtures from "@/__fixtures__/initialProducts";
import { setupStore } from "../store";
const CarpetRoleSvg = require("@/assets/images/icons/icon-carpet-roll.svg");
const CarpetRoleSelectedSvg = require("@/assets/images/icons/icon-carpet-roll-selected.svg");
const CarpetRollIcon = ({ selected }: { selected: boolean }) => {
return selected ? CarpetRoleSelectedSvg : CarpetRoleSvg;
};
export default function TabLayout() {
const colorScheme = useColorScheme();
@ -17,24 +23,40 @@ export default function TabLayout() {
<Provider store={store}>
<Tabs
screenOptions={{
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
tabBarActiveTintColor: Colors[colorScheme ?? "light"].tint,
headerShown: false,
}}>
}}
>
<Tabs.Screen
name="index"
options={{
title: 'Measure',
title: "Plywood",
tabBarIcon: ({ color, focused }) => (
<TabBarIcon name={focused ? 'scale' : 'scale-outline'} color={color} />
<TabBarIcon
name={focused ? "scale" : "scale-outline"}
color={color}
/>
),
}}
/>
<Tabs.Screen
name="product-editor"
options={{
title: 'Edit Products',
title: "Carpet Roll Calculator",
tabBarIcon: ({ color, focused }) => (
<TabBarIcon name={focused ? 'list' : 'list-outline'} color={color} />
<CarpetRollIcon selected={focused} />
),
}}
/>
<Tabs.Screen
name="product-editor"
options={{
title: "Edit Products",
tabBarIcon: ({ color, focused }) => (
<TabBarIcon
name={focused ? "list" : "list-outline"}
color={color}
/>
),
}}
/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="142.37779mm"
height="154.51445mm"
viewBox="0 0 142.37779 154.51445"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="icon-carpet-roll-selected.svg"
inkscape:export-filename="../assets/images/icons/carpet-roll-64.png"
inkscape:export-xdpi="11.417511"
inkscape:export-ydpi="11.417511"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#585858"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.79909512"
inkscape:cx="427.98409"
inkscape:cy="198.34935"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="681"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-16.165914,9.5264309)">
<path
id="path1159"
style="color:#000000;fill:#666666;fill-rule:evenodd;stroke:#cccccc;stroke-width:5;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
sodipodi:type="inkscape:offset"
inkscape:radius="0.33110365"
inkscape:original="M 85.214844 -6.9941406 C 70.013101 -6.494485 55.139955 -0.43163169 43.707031 9.5605469 C 30.013867 21.362005 20.944785 38.446198 19.085938 56.449219 C 17.358707 72.332698 21.014781 88.716233 29.230469 102.41016 C 39.520614 119.81061 56.638281 132.98252 76.03125 138.63672 C 82.65448 140.58173 89.540529 141.77025 96.458984 141.68555 C 115.61209 141.94239 134.76485 142.20153 153.91797 142.45703 C 154.55818 134.40231 155.19894 126.34763 155.83984 118.29297 C 135.18432 118.00696 114.52684 117.78015 93.873047 117.42578 C 80.647337 116.41488 67.916649 110.28207 58.582031 100.88867 C 52.070874 94.381035 47.193434 86.30892 44.728516 77.402344 C 42.589935 69.786839 42.32538 61.636547 44.011719 53.955078 C 46.999506 40.237159 56.336432 28.015139 68.902344 21.708984 C 76.179541 18.017237 84.56116 16.49232 92.609375 17.550781 C 105.47803 19.123751 117.29397 27.652918 122.69922 39.457031 C 124.2433 42.820885 125.26799 46.602101 125.57422 50.355469 C 126.08314 55.844903 125.04871 61.62513 122.51172 66.595703 C 118.69587 74.236827 111.48288 80.366569 102.99414 81.986328 C 98.380931 82.881417 93.437488 82.363391 89.1875 80.232422 C 83.668056 77.562868 79.16371 72.357272 78.208984 66.197266 C 77.642576 62.560594 78.60807 58.687652 80.996094 55.859375 C 80.898514 58.890704 81.095836 62.010499 82.367188 64.810547 C 84.05204 68.882567 87.592589 72.214241 91.904297 73.302734 C 95.777353 74.339207 99.867688 73.625621 103.61328 72.421875 C 106.30071 71.528147 108.63458 69.786802 110.41992 67.605469 C 114.58333 62.715455 116.25928 55.924687 115.11328 49.630859 C 113.74274 41.428115 107.89047 34.34961 100.46484 30.791016 C 92.86163 27.075438 83.717791 27.076984 75.933594 30.257812 C 64.775062 34.759192 56.518994 45.396663 54.443359 57.191406 C 52.491012 67.446627 54.957367 78.397692 60.941406 86.929688 C 68.583415 98.108042 81.45297 105.49998 94.955078 106.49414 C 106.54011 107.47506 118.38117 103.97767 127.69727 97.052734 C 140.03453 88.048478 148.27185 73.639108 149.67578 58.419922 C 151.00619 45.171814 147.4177 31.531028 139.85938 20.582031 C 130.27054 6.4471034 114.71504 -3.4768114 97.845703 -6.1816406 C 93.675003 -6.866132 89.439024 -7.1235453 85.214844 -6.9941406 z "
d="M 85.203125,-7.3242188 C 69.920576,-6.8219072 54.978371,-0.73159347 43.488281,9.3105469 29.730781,21.167938 20.62376,38.325311 18.755859,56.416016 c -1.734796,15.95712 1.938409,32.407948 10.191407,46.164064 10.334605,17.47423 27.516469,30.69674 46.990234,36.375 6.645281,1.95148 13.56116,3.1467 20.517578,3.0625 19.153082,0.25684 38.305822,0.51598 57.458982,0.77148 a 0.33113676,0.33113676 0 0 0 0.33399,-0.30664 c 0.64021,-8.0547 1.28097,-16.1094 1.92187,-24.16406 a 0.33113676,0.33113676 0 0 0 -0.32617,-0.35742 c -20.65485,-0.286 -41.30981,-0.51288 -61.960938,-0.86719 h -0.0039 c -13.133341,-1.00893 -25.787797,-7.10439 -35.0625,-16.4375 a 0.33113676,0.33113676 0 0 0 0,-0.002 C 52.343603,94.184995 47.495562,86.16238 45.046875,77.314453 a 0.33113676,0.33113676 0 0 0 0,-0.002 C 42.922997,69.749353 42.661672,61.651864 44.335938,54.025391 47.301902,40.407665 56.579386,28.264581 69.050781,22.005859 a 0.33113676,0.33113676 0 0 0 0.002,-0.002 c 7.218526,-3.661983 15.535683,-5.174225 23.513672,-4.125 a 0.33113676,0.33113676 0 0 0 0.002,0 c 12.749801,1.558443 24.475741,10.023864 29.830081,21.716797 1.528,3.328833 2.54287,7.075443 2.8457,10.787109 a 0.33113676,0.33113676 0 0 0 0,0.0039 c 0.50314,5.427095 -0.52104,11.148138 -3.02734,16.058593 a 0.33113676,0.33113676 0 0 0 -0.002,0.002 c -3.77296,7.555238 -10.91161,13.615485 -19.2832,15.21289 a 0.33113676,0.33113676 0 0 0 0,0.002 c -4.546338,0.882115 -9.418457,0.367934 -13.595702,-1.726562 a 0.33113676,0.33113676 0 0 0 -0.0039,-0.002 c -5.431125,-2.626837 -9.860013,-7.754966 -10.794922,-13.78711 -0.497265,-3.192735 0.282359,-6.536996 2.125,-9.185546 -0.0139,2.705038 0.257235,5.460018 1.404297,7.986328 1.724833,4.159718 5.336057,7.559506 9.757813,8.675781 3.964246,1.059122 8.116269,0.326271 11.890621,-0.886719 a 0.33113676,0.33113676 0 0 0 0.002,0 c 2.75294,-0.915515 5.13542,-2.694581 6.95508,-4.916016 l 0.004,-0.0059 c 4.22887,-4.970422 5.92559,-11.850563 4.76367,-18.238281 v -0.0039 c -1.39169,-8.320828 -7.31354,-15.475053 -14.83007,-19.078125 l -0.002,-0.002 C 92.910555,26.731743 83.676017,26.736335 75.808594,29.951172 64.535605,34.499437 56.212806,45.226465 54.117188,57.134766 c -1.968025,10.346436 0.517884,21.379932 6.552734,29.984375 7.700829,11.262849 20.653527,18.702169 34.257812,19.705079 11.668956,0.98802 23.584106,-2.53175 32.964846,-9.503908 l 0.002,-0.002 c 12.41469,-9.061773 20.69784,-23.548746 22.11133,-38.865234 v -0.002 c 1.33798,-13.328328 -2.27053,-27.040971 -9.87305,-38.054688 v -0.002 C 130.49093,6.1822132 114.86084,-3.7880615 97.898438,-6.5078125 93.705473,-7.195958 89.449462,-7.4542424 85.205078,-7.3242188 a 0.33113676,0.33113676 0 0 0 -0.002,0 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="142.37779mm"
height="154.51445mm"
viewBox="0 0 142.37779 154.51445"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="icon-carpet-roll.svg"
inkscape:export-filename="../assets/images/icons/carpet-roll-64.png"
inkscape:export-xdpi="11.417511"
inkscape:export-ydpi="11.417511"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#585858"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.79909512"
inkscape:cx="427.98409"
inkscape:cy="198.34935"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="681"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-16.165914,9.5264309)">
<path
id="path1159"
style="color:#000000;fill:#000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:5;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
sodipodi:type="inkscape:offset"
inkscape:radius="0.33110365"
inkscape:original="M 85.214844 -6.9941406 C 70.013101 -6.494485 55.139955 -0.43163169 43.707031 9.5605469 C 30.013867 21.362005 20.944785 38.446198 19.085938 56.449219 C 17.358707 72.332698 21.014781 88.716233 29.230469 102.41016 C 39.520614 119.81061 56.638281 132.98252 76.03125 138.63672 C 82.65448 140.58173 89.540529 141.77025 96.458984 141.68555 C 115.61209 141.94239 134.76485 142.20153 153.91797 142.45703 C 154.55818 134.40231 155.19894 126.34763 155.83984 118.29297 C 135.18432 118.00696 114.52684 117.78015 93.873047 117.42578 C 80.647337 116.41488 67.916649 110.28207 58.582031 100.88867 C 52.070874 94.381035 47.193434 86.30892 44.728516 77.402344 C 42.589935 69.786839 42.32538 61.636547 44.011719 53.955078 C 46.999506 40.237159 56.336432 28.015139 68.902344 21.708984 C 76.179541 18.017237 84.56116 16.49232 92.609375 17.550781 C 105.47803 19.123751 117.29397 27.652918 122.69922 39.457031 C 124.2433 42.820885 125.26799 46.602101 125.57422 50.355469 C 126.08314 55.844903 125.04871 61.62513 122.51172 66.595703 C 118.69587 74.236827 111.48288 80.366569 102.99414 81.986328 C 98.380931 82.881417 93.437488 82.363391 89.1875 80.232422 C 83.668056 77.562868 79.16371 72.357272 78.208984 66.197266 C 77.642576 62.560594 78.60807 58.687652 80.996094 55.859375 C 80.898514 58.890704 81.095836 62.010499 82.367188 64.810547 C 84.05204 68.882567 87.592589 72.214241 91.904297 73.302734 C 95.777353 74.339207 99.867688 73.625621 103.61328 72.421875 C 106.30071 71.528147 108.63458 69.786802 110.41992 67.605469 C 114.58333 62.715455 116.25928 55.924687 115.11328 49.630859 C 113.74274 41.428115 107.89047 34.34961 100.46484 30.791016 C 92.86163 27.075438 83.717791 27.076984 75.933594 30.257812 C 64.775062 34.759192 56.518994 45.396663 54.443359 57.191406 C 52.491012 67.446627 54.957367 78.397692 60.941406 86.929688 C 68.583415 98.108042 81.45297 105.49998 94.955078 106.49414 C 106.54011 107.47506 118.38117 103.97767 127.69727 97.052734 C 140.03453 88.048478 148.27185 73.639108 149.67578 58.419922 C 151.00619 45.171814 147.4177 31.531028 139.85938 20.582031 C 130.27054 6.4471034 114.71504 -3.4768114 97.845703 -6.1816406 C 93.675003 -6.866132 89.439024 -7.1235453 85.214844 -6.9941406 z "
d="M 85.203125,-7.3242188 C 69.920576,-6.8219072 54.978371,-0.73159347 43.488281,9.3105469 29.730781,21.167938 20.62376,38.325311 18.755859,56.416016 c -1.734796,15.95712 1.938409,32.407948 10.191407,46.164064 10.334605,17.47423 27.516469,30.69674 46.990234,36.375 6.645281,1.95148 13.56116,3.1467 20.517578,3.0625 19.153082,0.25684 38.305822,0.51598 57.458982,0.77148 a 0.33113676,0.33113676 0 0 0 0.33399,-0.30664 c 0.64021,-8.0547 1.28097,-16.1094 1.92187,-24.16406 a 0.33113676,0.33113676 0 0 0 -0.32617,-0.35742 c -20.65485,-0.286 -41.30981,-0.51288 -61.960938,-0.86719 h -0.0039 c -13.133341,-1.00893 -25.787797,-7.10439 -35.0625,-16.4375 a 0.33113676,0.33113676 0 0 0 0,-0.002 C 52.343603,94.184995 47.495562,86.16238 45.046875,77.314453 a 0.33113676,0.33113676 0 0 0 0,-0.002 C 42.922997,69.749353 42.661672,61.651864 44.335938,54.025391 47.301902,40.407665 56.579386,28.264581 69.050781,22.005859 a 0.33113676,0.33113676 0 0 0 0.002,-0.002 c 7.218526,-3.661983 15.535683,-5.174225 23.513672,-4.125 a 0.33113676,0.33113676 0 0 0 0.002,0 c 12.749801,1.558443 24.475741,10.023864 29.830081,21.716797 1.528,3.328833 2.54287,7.075443 2.8457,10.787109 a 0.33113676,0.33113676 0 0 0 0,0.0039 c 0.50314,5.427095 -0.52104,11.148138 -3.02734,16.058593 a 0.33113676,0.33113676 0 0 0 -0.002,0.002 c -3.77296,7.555238 -10.91161,13.615485 -19.2832,15.21289 a 0.33113676,0.33113676 0 0 0 0,0.002 c -4.546338,0.882115 -9.418457,0.367934 -13.595702,-1.726562 a 0.33113676,0.33113676 0 0 0 -0.0039,-0.002 c -5.431125,-2.626837 -9.860013,-7.754966 -10.794922,-13.78711 -0.497265,-3.192735 0.282359,-6.536996 2.125,-9.185546 -0.0139,2.705038 0.257235,5.460018 1.404297,7.986328 1.724833,4.159718 5.336057,7.559506 9.757813,8.675781 3.964246,1.059122 8.116269,0.326271 11.890621,-0.886719 a 0.33113676,0.33113676 0 0 0 0.002,0 c 2.75294,-0.915515 5.13542,-2.694581 6.95508,-4.916016 l 0.004,-0.0059 c 4.22887,-4.970422 5.92559,-11.850563 4.76367,-18.238281 v -0.0039 c -1.39169,-8.320828 -7.31354,-15.475053 -14.83007,-19.078125 l -0.002,-0.002 C 92.910555,26.731743 83.676017,26.736335 75.808594,29.951172 64.535605,34.499437 56.212806,45.226465 54.117188,57.134766 c -1.968025,10.346436 0.517884,21.379932 6.552734,29.984375 7.700829,11.262849 20.653527,18.702169 34.257812,19.705079 11.668956,0.98802 23.584106,-2.53175 32.964846,-9.503908 l 0.002,-0.002 c 12.41469,-9.061773 20.69784,-23.548746 22.11133,-38.865234 v -0.002 c 1.33798,-13.328328 -2.27053,-27.040971 -9.87305,-38.054688 v -0.002 C 130.49093,6.1822132 114.86084,-3.7880615 97.898438,-6.5078125 93.705473,-7.195958 89.449462,-7.4542424 85.205078,-7.3242188 a 0.33113676,0.33113676 0 0 0 -0.002,0 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -1,17 +1,12 @@
import { area_t } from "@/lib/dimensions";
import { Product } from "@/lib/product";
import convert, { Area, Length } from "convert";
import dayjs, { Dayjs } from "dayjs";
import { StyleSheet, Text, View } from "react-native";
export type AreaRugTagProps = {
dimensions: area_t,
price_per_area: {
price: number,
per: {
n: number,
u: Area,
}
},
product: Product,
date?: Dayjs
currencySymbol?: string
};
@ -19,17 +14,17 @@ export type AreaRugTagProps = {
export const AreaRugTag = (props: AreaRugTagProps) => {
const date = props.date || dayjs();
const square = props.dimensions.l * props.dimensions.w;
const areaUnits = `square ${props.dimensions.u}`;
const square2 = convert(square, areaUnits as Area).to(props.price_per_area.per.u)
const price = (square2 / props.price_per_area.per.n) * props.price_per_area.price;
const areaUnits = `sq ${props.dimensions.u}`;
const square2 = convert(square, areaUnits as Area).to("sq " + props.product.dimensions.u as Area)
const price = (square2 / props.product.pricePerUnit) * props.product.pricePerUnit;
const sPrice = price.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
const currencySymbol = props.currencySymbol || "$";
return (
<View style={styles.component}>
<Text aria-label="area rug dimensions" style={styles.dimensions}>{props.dimensions.l} x {props.dimensions.w}</Text>
<View aria-label="area rug tag" style={styles.component}>
<Text aria-label="area rug dimensions" style={styles.dimensions}>{Math.round(props.dimensions.l)} x {Math.round(props.dimensions.w)}</Text>
<Text aria-label="area rug price" style={styles.price}>{currencySymbol} {sPrice}</Text>
<Text aria-label="area rug date" style={styles.date}>{date.format("YYYY/MM/DD")}</Text>
<Text aria-label="this week's color" style={styles.tagColor}>[Curent Tag Color]</Text>

View File

@ -0,0 +1,106 @@
import React, { useEffect, useState } from "react";
import { View, Text, TextInput, Button, StyleSheet } from "react-native";
import {
productPriceFor,
priceDisplay,
pricePerUnitDisplay,
Product,
} from "@/lib/product";
import { selectProducts } from "@/features/product/productSlice";
import { area_t, diameterToLength, length_t } from "@/lib/dimensions";
import { useAppSelector } from "../app/store";
import { AreaRugTag } from "@/components/AreaRugTag";
import { Length } from "convert";
import ProductList from "@/components/ProductList";
const DEFAULT_UNIT: Length = "ft";
export const CarpetRollCalculator = () => {
const products = useAppSelector(selectProducts);
const [width, setWidth] = useState(0);
const [outerDiameter, setOuterDiameter] = useState<length_t>({
l: 0,
u: DEFAULT_UNIT,
});
const [innerDiameter, setInnerDiameter] = useState<length_t>({
l: 0,
u: DEFAULT_UNIT,
});
const [numRings, setNumRings] = useState(0);
const [price, setPrice] = useState(0);
const [rugDimensions, setRugDimensions] = useState<area_t>({
u: DEFAULT_UNIT,
w: 0,
l: 0,
});
const [selectedProduct, setSelectedProduct] = useState<Product | null>(null);
const [units, setUnits] = useState<Length>(DEFAULT_UNIT);
useEffect(() => {
console.log(`recalculating...`);
const dimens = {
l: diameterToLength(outerDiameter, innerDiameter, numRings).l || 0.0,
w: width || selectedProduct?.dimensions.l || 0.0,
u: units || selectedProduct?.dimensions.u || "ft",
};
console.dir(dimens);
setRugDimensions(dimens);
}, [outerDiameter, innerDiameter, width, numRings, selectedProduct, units]);
return (
<View style={styles.container}>
{selectedProduct && (
<AreaRugTag dimensions={rugDimensions} product={selectedProduct} />
)}
<View>
<Text>Length Calculation</Text>
<View>
<Text>Outer Diameter:</Text>
<TextInput
aria-label="outer diameter"
onChangeText={(text) =>
setOuterDiameter({ l: Number(text), u: units })
}
/>
<Text>Inner Diameter:</Text>
<TextInput
aria-label="inner diameter"
onChangeText={(text) =>
setInnerDiameter({ l: Number(text), u: units })
}
/>
<Text>Number of rings:</Text>
<TextInput
aria-label="number of rings"
onChangeText={(text) => setNumRings(Number(text))}
/>
</View>
</View>
<View>
<Text>Width:</Text>
<TextInput
aria-label="width"
onChangeText={(text) => setWidth(Number(text))}
/>
</View>
<Text>Price: {priceDisplay(price)}</Text>
<Text>
{selectedProduct ? pricePerUnitDisplay(selectedProduct) : "0.00"}
</Text>
<View style={styles.container}>
<ProductList onProductSelected={setSelectedProduct} />
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
padding: 20,
},
});
export default CarpetRollCalculator;

View File

@ -1,22 +1,26 @@
import { ScrollView, StyleSheet } from "react-native";
import { ProductTile } from "./ProductTile";
import { Product } from "@/lib/product";
import { Product, product_type_t } from "@/lib/product";
import { useState } from "react";
import { selectProducts } from "@/features/product/productSlice";
import { useAppSelector } from "@/app/store";
export type ProductSelectionProps = {
onProductSelected?: (product: Product) => any;
productType?: product_type_t;
};
export default function ProductList({
productType,
onProductSelected,
}: ProductSelectionProps) {
const [activeProduct, setActiveProduct] = useState(null as null | Product);
const products = useAppSelector(selectProducts).filter(p => !!p).filter((p) => {
console.dir(p);
return !!p.dimensions;
});
const products = useAppSelector(selectProducts)
.filter((p) => !!p)
.filter((p: Product) => productType ? p.type === productType : true)
.filter((p) => {
return !!p.dimensions;
});
function doOnProductSelected(product: Product) {
setActiveProduct(product);

View File

@ -28,6 +28,7 @@ export function ProductTile ({product, onProductSelected, isActive} : ProductTil
return (
<TouchableHighlight
aria-label={`product ${product.id}`}
style={styles[k].highlight}
onPress={() => onProductSelected && onProductSelected(product)}>
<Text style={styles[k].text}>{product.attributes?.name || `Product ${product.id}`} ({priceDisplay})</Text>

View File

@ -1,21 +1,23 @@
import React from 'react';
import { render, screen } from '@testing-library/react-native';
import { AreaRugTag } from '@/components/AreaRugTag';
import { area_t } from '@/lib/dimensions';
import dayjs from 'dayjs';
import { Area } from 'convert';
import initialProducts from '@/__fixtures__/initialProducts';
import { Product } from '@/lib/product';
describe('AreaRugTag', () => {
it('renders correctly with dimensions, price per area, date and currency symbol', () => {
const dimensions: area_t = { l: 10, w: 20, u: 'foot' };
const pricePerArea = { price: 100, per: { n: 1, u: 'square foot' as Area } };
const dimensions: area_t = { l: 10, w: 20, u: 'ft' };
const date = dayjs();
const currencySymbol = '$';
const product = initialProducts.find(p => "area_rug" === p.type) as Product;
render(
<AreaRugTag
dimensions={dimensions}
price_per_area={pricePerArea}
product={product}
date={date}
currencySymbol={currencySymbol}
/>

View File

@ -0,0 +1,44 @@
import React from "react";
import { render, fireEvent, screen, within } from "@testing-library/react-native";
import CarpetRollCalculator from "@/components/CarpetRollCalculator";
import { renderWithProviders } from "@/lib/rendering";
import allProducts from "@/__fixtures__/initialProducts";
import { Product, pricePerUnitDisplay } from "@/lib/product";
import initialProducts from "@/__fixtures__/initialProducts";
const areaRugProducts = allProducts.filter((p) => "area_rug" === p.type);
describe("CarpetRollCalculator", () => {
it("should render correctly", () => {
renderWithProviders(<CarpetRollCalculator />, {
products: initialProducts,
});
const areaRug = initialProducts.find(p => p.type === 'area_rug') as Product;
const areaRugLabel = `product ${areaRug.id}`;
fireEvent.press(screen.getByLabelText(areaRugLabel));
// Test the interaction with the width input
const widthInput = screen.getByLabelText("width");
fireEvent.changeText(widthInput, "10");
// Test the interaction with the outer diameter input
const outerDiameterInput = screen.getByLabelText("outer diameter");
fireEvent.changeText(outerDiameterInput, "3");
// Test the interaction with the inner diameter input
const innerDiameterInput = screen.getByLabelText("inner diameter");
fireEvent.changeText(innerDiameterInput, "1");
// Test the interaction with the number of rings input
const numRingsInput = screen.getByLabelText("number of rings");
fireEvent.changeText(numRingsInput, "5");
jest.advanceTimersByTime(3000);
// Test the interaction with the price display
const {getByText} = within(screen.getByLabelText("area rug price"));
expect(getByText(/\$.*58.*\..*19.*/)).toBeTruthy();
});
});

View File

@ -2,34 +2,27 @@ import { render, fireEvent, screen, act, within } from '@testing-library/react-n
import { Provider } from 'react-redux';
import ProductCalculatorSelector from '@/components/ProductCalculatorSelector';
import { renderWithProviders } from '@/lib/rendering';
import { Product } from '@/lib/product';
import { Product, pricePerUnitDisplay, productPriceFor } from '@/lib/product';
import initialProducts from '@/__fixtures__/initialProducts';
const mockAreaProduct = initialProducts.find(p => 'w' in p.dimensions ) as Product
const mockLengthProduct = initialProducts.find(p => (!('w' in p.dimensions)) ) as Product
describe('ProductCalculatorSelector', () => {
const mockAreaProduct = new Product(
100,
{ l: 4, w: 8, u: "ft" },
{"name": "area product"},
);
const mockLengthProduct = new Product(
100,
{ l: 4, u: "ft" },
{"name": "length product"},
);
it('renders correctly', () => {
renderWithProviders(
(<ProductCalculatorSelector />),
{
products: [
mockAreaProduct.asObject,
mockLengthProduct.asObject,
mockAreaProduct,
mockLengthProduct,
],
}
)
expect(screen.getByText('Please select a product')).toBeTruthy();
const label = `${mockAreaProduct.attributes.name} (${mockAreaProduct.pricePerUnitDisplay})`;
const label = `${mockAreaProduct.attributes?.name} (${pricePerUnitDisplay(mockAreaProduct)})`;
expect(screen.getByText(label)).toBeTruthy();
});
@ -38,15 +31,14 @@ describe('ProductCalculatorSelector', () => {
(<ProductCalculatorSelector />),
{
products: [
mockLengthProduct.asObject,
mockAreaProduct.asObject,
mockLengthProduct,
mockAreaProduct,
]
}
);
expect(screen.getByText('Please select a product')).toBeTruthy();
const areaLabel = `${mockAreaProduct.attributes.name} (${mockAreaProduct.pricePerUnitDisplay})`;
const lengthLabel = `${mockLengthProduct.attributes.name} (${mockLengthProduct.pricePerUnitDisplay})`;
const areaLabel = `${mockAreaProduct.attributes?.name} (${pricePerUnitDisplay(mockAreaProduct)})`;
fireEvent.press(screen.getByText(areaLabel));
const lengthInput = screen.getByLabelText("enter length");
@ -63,10 +55,10 @@ describe('ProductCalculatorSelector', () => {
jest.advanceTimersByTime(3000);
const price = mockAreaProduct.priceFor({l: 2, w: 4, u: "ft"});
const price = productPriceFor(mockAreaProduct, {l: 2, w: 4, u: "ft"})
const sPrice = price.toLocaleString(undefined, {maximumFractionDigits: 2, minimumFractionDigits: 2,});
const element = screen.getByLabelText("calculated price");
const {getByText} = within(element);
expect(getByText(sPrice)).toBeTruthy();
expect(getByText(/\$.*0.*\.10/)).toBeTruthy();
});
});

View File

@ -1,6 +1,6 @@
import React from 'react';
import { renderWithProviders } from '@/lib/rendering';
import { Product } from '@/lib/product';
import { Product, pricePerUnitDisplay } from '@/lib/product';
import ProductList from '@/components/ProductList';
import initialProducts from '@/__fixtures__/initialProducts';
import { screen } from '@testing-library/react-native';
@ -15,13 +15,24 @@ describe('ProductList', () => {
});
it('renders products correctly', () => {
const mockProduct = initialProducts[0];
const mockProduct = initialProducts[0] as Product;
const label = `${mockProduct.attributes?.name} (${pricePerUnitDisplay(mockProduct)})`;
const { getByText } = renderWithProviders(<ProductList />, {
products: [mockProduct],
});
expect(getByText(label)).toBeTruthy();
});
expect(getByText(mockProduct.attributes.name)).toBeTruthy();
expect(getByText(`$${mockProduct.pricePerUnit}`)).toBeTruthy();
it('renders only area_rug products', () => {
const areaRug = initialProducts.find(p => p.type == "area_rug") as Product;
const label = `${areaRug?.attributes?.name} (${pricePerUnitDisplay(areaRug)})`;
renderWithProviders(<ProductList productType='area_rug' />, {
products: initialProducts,
});
expect(screen.getByText(label)).toBeTruthy();
});
});

View File

@ -1,7 +1,7 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import UnitChooser from "../UnitChooser";
import { Length } from 'safe-units';
import { Length } from 'convert';
describe('UnitChooser', () => {
const mockOnChoicePressed = jest.fn();

View File

@ -1,12 +1,12 @@
import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit';
import { Id, Product } from '@/lib/product';
import { dimensions_t, ProductData } from "@/lib/dimensions_t";
import { dimensions_t, } from "@/lib/dimensions";
import uuid from "react-native-uuid";
import { RootState } from '@/app/store';
import { Length } from 'convert';
const initialState = {
products: [] as ProductData[],
products: [] as Product[],
units: "ft",
};

View File

@ -45,7 +45,7 @@ export type AreaRugProduct = Product & {
type: "lumber"
}
export function productPriceFor(product : Product, dimensions: dimensions_t, damage: number): number {
export function productPriceFor(product : Product, dimensions: dimensions_t, damage: number = 0): number {
if (Number.isNaN(damage)) damage = 0;
const dim = matchDimensions(dimensions, product.dimensions);
return (

View File

@ -54,6 +54,7 @@
"@babel/core": "^7.20.0",
"@babel/preset-typescript": "^7.24.7",
"@testing-library/react-native": "^12.5.1",
"@testing-library/user-event": "^14.5.2",
"@types/jest": "^29.5.12",
"@types/react": "~18.2.45",
"@types/react-test-renderer": "^18.0.7",

View File

@ -124,6 +124,9 @@ devDependencies:
'@testing-library/react-native':
specifier: ^12.5.1
version: 12.5.1(jest@29.7.0)(react-native@0.74.3)(react-test-renderer@18.2.0)(react@18.2.0)
'@testing-library/user-event':
specifier: ^14.5.2
version: 14.5.2(@testing-library/dom@10.4.0)
'@types/jest':
specifier: ^29.5.12
version: 29.5.12
@ -2977,6 +2980,20 @@ packages:
dependencies:
'@sinonjs/commons': 3.0.1
/@testing-library/dom@10.4.0:
resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==, tarball: https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz}
engines: {node: '>=18'}
dependencies:
'@babel/code-frame': 7.24.7
'@babel/runtime': 7.24.7
'@types/aria-query': 5.0.4
aria-query: 5.3.0
chalk: 4.1.2
dom-accessibility-api: 0.5.16
lz-string: 1.5.0
pretty-format: 27.5.1
dev: true
/@testing-library/react-native@12.5.1(jest@29.7.0)(react-native@0.74.3)(react-test-renderer@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-PApr3f6DmSJF/EIiWYZfcBzuy6w7fK8TW4a6KfQHTeAcfZ6lADtRO7R0QM5WI+b7tJ33JvIPgzCg1MiuRz4v0g==, tarball: https://registry.npmjs.org/@testing-library/react-native/-/react-native-12.5.1.tgz}
peerDependencies:
@ -2997,11 +3014,24 @@ packages:
redent: 3.0.0
dev: true
/@testing-library/user-event@14.5.2(@testing-library/dom@10.4.0):
resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==, tarball: https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz}
engines: {node: '>=12', npm: '>=6'}
peerDependencies:
'@testing-library/dom': '>=7.21.4'
dependencies:
'@testing-library/dom': 10.4.0
dev: true
/@tootallnate/once@2.0.0:
resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==, tarball: https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz}
engines: {node: '>= 10'}
dev: true
/@types/aria-query@5.0.4:
resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==, tarball: https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz}
dev: true
/@types/babel__core@7.20.5:
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==, tarball: https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz}
dependencies:
@ -3346,6 +3376,12 @@ packages:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, tarball: https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz}
dev: false
/aria-query@5.3.0:
resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==, tarball: https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz}
dependencies:
dequal: 2.0.3
dev: true
/array-buffer-byte-length@1.0.1:
resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==, tarball: https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz}
engines: {node: '>= 0.4'}
@ -4252,6 +4288,11 @@ packages:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==, tarball: https://registry.npmjs.org/depd/-/depd-2.0.0.tgz}
engines: {node: '>= 0.8'}
/dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==, tarball: https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz}
engines: {node: '>=6'}
dev: true
/destroy@1.2.0:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==, tarball: https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
@ -4279,6 +4320,10 @@ packages:
path-type: 4.0.0
dev: false
/dom-accessibility-api@0.5.16:
resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==, tarball: https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz}
dev: true
/domexception@4.0.0:
resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==, tarball: https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz}
engines: {node: '>=12'}
@ -6524,6 +6569,11 @@ packages:
yallist: 4.0.0
dev: false
/lz-string@1.5.0:
resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==, tarball: https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz}
hasBin: true
dev: true
/make-dir@2.1.0:
resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==, tarball: https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz}
engines: {node: '>=6'}
@ -7399,6 +7449,15 @@ packages:
ansi-styles: 4.3.0
react-is: 17.0.2
/pretty-format@27.5.1:
resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==, tarball: https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
dependencies:
ansi-regex: 5.0.1
ansi-styles: 5.2.0
react-is: 17.0.2
dev: true
/pretty-format@29.7.0:
resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==, tarball: https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}

54
svg/icon-carpet-roll.svg Normal file
View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="142.37779mm"
height="154.51445mm"
viewBox="0 0 142.37779 154.51445"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="icon-carpet-roll.svg"
inkscape:export-filename="../assets/images/icons/carpet-roll-64.png"
inkscape:export-xdpi="11.417511"
inkscape:export-ydpi="11.417511"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#585858"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.79909512"
inkscape:cx="427.98409"
inkscape:cy="198.34935"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="681"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-16.165914,9.5264309)">
<path
id="path1159"
style="color:#000000;fill:#000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:5;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
sodipodi:type="inkscape:offset"
inkscape:radius="0.33110365"
inkscape:original="M 85.214844 -6.9941406 C 70.013101 -6.494485 55.139955 -0.43163169 43.707031 9.5605469 C 30.013867 21.362005 20.944785 38.446198 19.085938 56.449219 C 17.358707 72.332698 21.014781 88.716233 29.230469 102.41016 C 39.520614 119.81061 56.638281 132.98252 76.03125 138.63672 C 82.65448 140.58173 89.540529 141.77025 96.458984 141.68555 C 115.61209 141.94239 134.76485 142.20153 153.91797 142.45703 C 154.55818 134.40231 155.19894 126.34763 155.83984 118.29297 C 135.18432 118.00696 114.52684 117.78015 93.873047 117.42578 C 80.647337 116.41488 67.916649 110.28207 58.582031 100.88867 C 52.070874 94.381035 47.193434 86.30892 44.728516 77.402344 C 42.589935 69.786839 42.32538 61.636547 44.011719 53.955078 C 46.999506 40.237159 56.336432 28.015139 68.902344 21.708984 C 76.179541 18.017237 84.56116 16.49232 92.609375 17.550781 C 105.47803 19.123751 117.29397 27.652918 122.69922 39.457031 C 124.2433 42.820885 125.26799 46.602101 125.57422 50.355469 C 126.08314 55.844903 125.04871 61.62513 122.51172 66.595703 C 118.69587 74.236827 111.48288 80.366569 102.99414 81.986328 C 98.380931 82.881417 93.437488 82.363391 89.1875 80.232422 C 83.668056 77.562868 79.16371 72.357272 78.208984 66.197266 C 77.642576 62.560594 78.60807 58.687652 80.996094 55.859375 C 80.898514 58.890704 81.095836 62.010499 82.367188 64.810547 C 84.05204 68.882567 87.592589 72.214241 91.904297 73.302734 C 95.777353 74.339207 99.867688 73.625621 103.61328 72.421875 C 106.30071 71.528147 108.63458 69.786802 110.41992 67.605469 C 114.58333 62.715455 116.25928 55.924687 115.11328 49.630859 C 113.74274 41.428115 107.89047 34.34961 100.46484 30.791016 C 92.86163 27.075438 83.717791 27.076984 75.933594 30.257812 C 64.775062 34.759192 56.518994 45.396663 54.443359 57.191406 C 52.491012 67.446627 54.957367 78.397692 60.941406 86.929688 C 68.583415 98.108042 81.45297 105.49998 94.955078 106.49414 C 106.54011 107.47506 118.38117 103.97767 127.69727 97.052734 C 140.03453 88.048478 148.27185 73.639108 149.67578 58.419922 C 151.00619 45.171814 147.4177 31.531028 139.85938 20.582031 C 130.27054 6.4471034 114.71504 -3.4768114 97.845703 -6.1816406 C 93.675003 -6.866132 89.439024 -7.1235453 85.214844 -6.9941406 z "
d="M 85.203125,-7.3242188 C 69.920576,-6.8219072 54.978371,-0.73159347 43.488281,9.3105469 29.730781,21.167938 20.62376,38.325311 18.755859,56.416016 c -1.734796,15.95712 1.938409,32.407948 10.191407,46.164064 10.334605,17.47423 27.516469,30.69674 46.990234,36.375 6.645281,1.95148 13.56116,3.1467 20.517578,3.0625 19.153082,0.25684 38.305822,0.51598 57.458982,0.77148 a 0.33113676,0.33113676 0 0 0 0.33399,-0.30664 c 0.64021,-8.0547 1.28097,-16.1094 1.92187,-24.16406 a 0.33113676,0.33113676 0 0 0 -0.32617,-0.35742 c -20.65485,-0.286 -41.30981,-0.51288 -61.960938,-0.86719 h -0.0039 c -13.133341,-1.00893 -25.787797,-7.10439 -35.0625,-16.4375 a 0.33113676,0.33113676 0 0 0 0,-0.002 C 52.343603,94.184995 47.495562,86.16238 45.046875,77.314453 a 0.33113676,0.33113676 0 0 0 0,-0.002 C 42.922997,69.749353 42.661672,61.651864 44.335938,54.025391 47.301902,40.407665 56.579386,28.264581 69.050781,22.005859 a 0.33113676,0.33113676 0 0 0 0.002,-0.002 c 7.218526,-3.661983 15.535683,-5.174225 23.513672,-4.125 a 0.33113676,0.33113676 0 0 0 0.002,0 c 12.749801,1.558443 24.475741,10.023864 29.830081,21.716797 1.528,3.328833 2.54287,7.075443 2.8457,10.787109 a 0.33113676,0.33113676 0 0 0 0,0.0039 c 0.50314,5.427095 -0.52104,11.148138 -3.02734,16.058593 a 0.33113676,0.33113676 0 0 0 -0.002,0.002 c -3.77296,7.555238 -10.91161,13.615485 -19.2832,15.21289 a 0.33113676,0.33113676 0 0 0 0,0.002 c -4.546338,0.882115 -9.418457,0.367934 -13.595702,-1.726562 a 0.33113676,0.33113676 0 0 0 -0.0039,-0.002 c -5.431125,-2.626837 -9.860013,-7.754966 -10.794922,-13.78711 -0.497265,-3.192735 0.282359,-6.536996 2.125,-9.185546 -0.0139,2.705038 0.257235,5.460018 1.404297,7.986328 1.724833,4.159718 5.336057,7.559506 9.757813,8.675781 3.964246,1.059122 8.116269,0.326271 11.890621,-0.886719 a 0.33113676,0.33113676 0 0 0 0.002,0 c 2.75294,-0.915515 5.13542,-2.694581 6.95508,-4.916016 l 0.004,-0.0059 c 4.22887,-4.970422 5.92559,-11.850563 4.76367,-18.238281 v -0.0039 c -1.39169,-8.320828 -7.31354,-15.475053 -14.83007,-19.078125 l -0.002,-0.002 C 92.910555,26.731743 83.676017,26.736335 75.808594,29.951172 64.535605,34.499437 56.212806,45.226465 54.117188,57.134766 c -1.968025,10.346436 0.517884,21.379932 6.552734,29.984375 7.700829,11.262849 20.653527,18.702169 34.257812,19.705079 11.668956,0.98802 23.584106,-2.53175 32.964846,-9.503908 l 0.002,-0.002 c 12.41469,-9.061773 20.69784,-23.548746 22.11133,-38.865234 v -0.002 c 1.33798,-13.328328 -2.27053,-27.040971 -9.87305,-38.054688 v -0.002 C 130.49093,6.1822132 114.86084,-3.7880615 97.898438,-6.5078125 93.705473,-7.195958 89.449462,-7.4542424 85.205078,-7.3242188 a 0.33113676,0.33113676 0 0 0 -0.002,0 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB