@aclymatepackages/modules 1.0.0

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 (45) hide show
  1. package/README.md +43 -0
  2. package/babel.config.json +18 -0
  3. package/dist/components/CompaniesAutocomplete.js +126 -0
  4. package/dist/components/CompanyOnboardingInput.js +116 -0
  5. package/dist/components/ComparisonChart.js +200 -0
  6. package/dist/components/CustomTooltipDisplayRow.js +59 -0
  7. package/dist/components/EmissionsChart.js +467 -0
  8. package/dist/components/EmissionsCustomTooltip.js +181 -0
  9. package/dist/components/EmissionsPieChart.js +130 -0
  10. package/dist/components/EmissionsReductionGraph.js +118 -0
  11. package/dist/components/FootprintEquivalencies.js +181 -0
  12. package/dist/components/FootprintVideo.js +262 -0
  13. package/dist/components/FuelTypesSelect.js +36 -0
  14. package/dist/components/IndustryAutocomplete.js +58 -0
  15. package/dist/components/PlacesAutocomplete.js +173 -0
  16. package/dist/components/StripeElements.js +95 -0
  17. package/dist/components/YesNoQuestion.js +88 -0
  18. package/dist/components/stripeInput.js +293 -0
  19. package/dist/components/useChartWarningLabels.js +143 -0
  20. package/dist/index.js +118 -0
  21. package/package.json +74 -0
  22. package/public/favicon.ico +0 -0
  23. package/public/index.html +43 -0
  24. package/public/logo192.png +0 -0
  25. package/public/logo512.png +0 -0
  26. package/public/manifest.json +25 -0
  27. package/public/robots.txt +3 -0
  28. package/src/components/CompaniesAutocomplete.js +125 -0
  29. package/src/components/CompanyOnboardingInput.js +113 -0
  30. package/src/components/ComparisonChart.js +236 -0
  31. package/src/components/CustomTooltipDisplayRow.js +41 -0
  32. package/src/components/EmissionsChart.js +579 -0
  33. package/src/components/EmissionsCustomTooltip.js +146 -0
  34. package/src/components/EmissionsPieChart.js +120 -0
  35. package/src/components/EmissionsReductionGraph.js +107 -0
  36. package/src/components/FootprintEquivalencies.js +203 -0
  37. package/src/components/FootprintVideo.js +328 -0
  38. package/src/components/FuelTypesSelect.js +29 -0
  39. package/src/components/IndustryAutocomplete.js +56 -0
  40. package/src/components/PlacesAutocomplete.js +174 -0
  41. package/src/components/StripeElements.js +95 -0
  42. package/src/components/YesNoQuestion.js +68 -0
  43. package/src/components/stripeInput.js +288 -0
  44. package/src/components/useChartWarningLabels.js +139 -0
  45. package/src/index.js +35 -0
@@ -0,0 +1,68 @@
1
+ import React from "react";
2
+
3
+ import { useTheme } from "@mui/styles";
4
+ import { Grid, Typography } from "@mui/material";
5
+ import { ThemeProvider } from "@mui/material/styles";
6
+
7
+ import { ToggleButtons } from "@aclymatepackages/atoms";
8
+ import { mergeDarkTheme, mainTheme } from "@aclymatepackages/themes";
9
+
10
+ const YesNoQuestionButtons = ({ value, setValue }) => {
11
+ const { palette } = useTheme();
12
+
13
+ const noButtonStyleObj =
14
+ value === false ? { style: { backgroundColor: palette.error.main } } : {};
15
+
16
+ const yesButtonStyleObj = value
17
+ ? { style: { backgroundColor: palette.secondary.main } }
18
+ : {};
19
+
20
+ const buttons = [
21
+ { value: false, name: "No", ...noButtonStyleObj },
22
+ { value: true, name: "Yes", ...yesButtonStyleObj },
23
+ ];
24
+
25
+ return <ToggleButtons value={value} onChange={setValue} buttons={buttons} />;
26
+ };
27
+
28
+ const YesNoQuestion = ({
29
+ question,
30
+ value,
31
+ setValue,
32
+ typographyProps,
33
+ lightBackground,
34
+ }) => {
35
+ const findTheme = () => {
36
+ if (lightBackground || value !== undefined) {
37
+ return mainTheme;
38
+ }
39
+
40
+ return mergeDarkTheme;
41
+ };
42
+
43
+ return (
44
+ <Grid
45
+ container
46
+ spacing={2}
47
+ justifyContent="space-between"
48
+ alignItems="center"
49
+ wrap="nowrap"
50
+ >
51
+ <Grid item>
52
+ <Typography
53
+ variant="body1"
54
+ color={lightBackground ? "textPrimary" : "inherit"}
55
+ {...typographyProps}
56
+ >
57
+ {question}
58
+ </Typography>
59
+ </Grid>
60
+ <Grid item>
61
+ <ThemeProvider theme={findTheme()}>
62
+ <YesNoQuestionButtons value={value} setValue={setValue} />
63
+ </ThemeProvider>
64
+ </Grid>
65
+ </Grid>
66
+ );
67
+ };
68
+ export default YesNoQuestion;
@@ -0,0 +1,288 @@
1
+ import React, { useState } from "react";
2
+ import dayjs from "dayjs";
3
+ import {
4
+ useStripe,
5
+ useElements,
6
+ PaymentElement,
7
+ } from "@stripe/react-stripe-js";
8
+
9
+ import { Box, Typography } from "@mui/material";
10
+
11
+ import { LoadingButton } from "@aclymatepackages/atoms";
12
+ import { isObjectEmpty } from "@aclymatepackages/other-helpers";
13
+ import fetchAclymateApi from "@aclymatepackages/fetch-aclymate-api";
14
+
15
+ const StripeSubmitButton = ({
16
+ stripeSubmit,
17
+ stripeError,
18
+ isStripeComplete,
19
+ buttonText,
20
+ }) => {
21
+ const [saveLoading, setSaveLoading] = useState(false);
22
+ const handleSave = async () => {
23
+ setSaveLoading(true);
24
+ await stripeSubmit();
25
+
26
+ return setSaveLoading(false);
27
+ };
28
+
29
+ return (
30
+ <LoadingButton
31
+ isLoading={saveLoading}
32
+ label={buttonText || "Save Payment Info"}
33
+ color="secondary"
34
+ disabled={!isStripeComplete || !isObjectEmpty(stripeError)}
35
+ onClick={() => handleSave()}
36
+ />
37
+ );
38
+ };
39
+
40
+ const CardInput = ({ options, setCardData }) => {
41
+ const defaultOptions = {
42
+ fields: {
43
+ billingDetails: {
44
+ name: "never",
45
+ email: "never",
46
+ },
47
+ },
48
+ wallets: {
49
+ applePay: "never",
50
+ googlePay: "never",
51
+ },
52
+ business: {
53
+ name: "Aclymate",
54
+ },
55
+ };
56
+
57
+ return (
58
+ <PaymentElement
59
+ options={options || defaultOptions}
60
+ onChange={(e) => setCardData(e)}
61
+ />
62
+ );
63
+ };
64
+
65
+ const StripeInputForm = ({ style, options, error = {}, setCardData }) => {
66
+ const { message } = error;
67
+ const CardError = () => (
68
+ <>
69
+ {message && (
70
+ <Typography variant="caption" color="error">
71
+ {message}
72
+ </Typography>
73
+ )}
74
+ </>
75
+ );
76
+
77
+ if (style) {
78
+ return (
79
+ <>
80
+ <Box p={2} style={style}>
81
+ <CardInput options={options} setCardData={setCardData} />
82
+ </Box>
83
+ <CardError />
84
+ </>
85
+ );
86
+ }
87
+
88
+ return (
89
+ <>
90
+ <CardInput options={options} setCardData={setCardData} />
91
+ <CardError />
92
+ </>
93
+ );
94
+ };
95
+
96
+ const useStripeInput = ({
97
+ onButtonClick,
98
+ accountData,
99
+ authUser,
100
+ updateAccountData,
101
+ confirmSetupReturnUrl = "",
102
+ baseFetchHost,
103
+ buttonText,
104
+ }) => {
105
+ const stripe = useStripe();
106
+ const elements = useElements();
107
+
108
+ const {
109
+ stripeCustomerId: accountObjStripeCustomerId,
110
+ billing = {},
111
+ name: accountName,
112
+ } = accountData || {};
113
+ const { stripeCustomerId: billingObjStripeCustomerId } = billing;
114
+
115
+ const [paymentData, setPaymentData] = useState({});
116
+ const { error: stripeError, complete: isStripeComplete } = paymentData;
117
+
118
+ const createBillingDetailsFullName = () => {
119
+ const { displayName } = authUser;
120
+ const [firstCompanyWord] = accountName.split(" ");
121
+ const [firstName, lastName] = displayName.split(" ");
122
+
123
+ if (!lastName) {
124
+ return `${firstName} ${firstCompanyWord}`;
125
+ }
126
+
127
+ return `${firstName} ${lastName}`;
128
+ };
129
+
130
+ const updateAccountBilling = async (updateObj) => {
131
+ await updateAccountData(updateObj);
132
+
133
+ if (onButtonClick) {
134
+ return await onButtonClick(updateObj);
135
+ }
136
+
137
+ return updateObj;
138
+ };
139
+
140
+ const createStripePaymentMethod = async () => {
141
+ const { email } = authUser;
142
+
143
+ const billingDetailsFullName = createBillingDetailsFullName();
144
+
145
+ const { setupIntent, error } = await stripe.confirmSetup({
146
+ elements,
147
+ confirmParams: {
148
+ return_url: confirmSetupReturnUrl,
149
+ payment_method_data: {
150
+ billing_details: {
151
+ name: billingDetailsFullName,
152
+ email,
153
+ },
154
+ },
155
+ },
156
+ redirect: "if_required",
157
+ });
158
+
159
+ if (error) {
160
+ setPaymentData((data) => ({ ...data, error }));
161
+ return false;
162
+ }
163
+
164
+ const { payment_method, status } = setupIntent;
165
+
166
+ const paymentMethod = await fetchAclymateApi({
167
+ path: "/stripe/get-payment-method-data",
168
+ method: "POST",
169
+ data: {
170
+ paymentMethodId: payment_method,
171
+ },
172
+ baseFetchHost,
173
+ callback: (res) => res,
174
+ });
175
+
176
+ return {
177
+ ...paymentMethod,
178
+ status,
179
+ };
180
+ };
181
+
182
+ const savePaymentMethod = async (paymentMethod) => {
183
+ const { paymentMethodId: existingPaymentMethodId, subscriptions } = billing;
184
+ const existingSubscriptionsObj = subscriptions ? { subscriptions } : {};
185
+ const { type } = paymentMethod;
186
+
187
+ if (existingPaymentMethodId && authUser) {
188
+ await fetchAclymateApi({
189
+ path: "/stripe/detach-payment-method",
190
+ method: "POST",
191
+ data: {
192
+ paymentMethodId: existingPaymentMethodId,
193
+ },
194
+ baseFetchHost,
195
+ });
196
+ }
197
+
198
+ const generateCardBillingAccountObj = () => {
199
+ const { card, id: newPaymentMethodId } = paymentMethod;
200
+ const { brand, exp_month, exp_year, last4 } = card;
201
+ const daysInMonth = dayjs(`${exp_year}-${exp_month}-01`).daysInMonth();
202
+ const expirationDate = dayjs(
203
+ `${exp_year}-${exp_month}-${daysInMonth}`
204
+ ).toDate();
205
+
206
+ return {
207
+ billing: {
208
+ card: {
209
+ brand,
210
+ expiration: `${exp_month}/${exp_year}`,
211
+ expirationDate,
212
+ last4,
213
+ },
214
+ paymentMethodId: newPaymentMethodId,
215
+ ...existingSubscriptionsObj,
216
+ },
217
+ stripeCustomerId:
218
+ billingObjStripeCustomerId || accountObjStripeCustomerId,
219
+ };
220
+ };
221
+
222
+ const generateBankBillingAccountObj = () => {
223
+ const {
224
+ us_bank_account,
225
+ id: newPaymentMethodId,
226
+ billing_details,
227
+ status,
228
+ } = paymentMethod;
229
+ const { name, email } = billing_details;
230
+ const {
231
+ account_type,
232
+ account_holder_type,
233
+ bank_name,
234
+ last4,
235
+ routing_number,
236
+ } = us_bank_account;
237
+
238
+ return {
239
+ billing: {
240
+ us_bank_account: {
241
+ account_type,
242
+ account_holder_type,
243
+ bank_name,
244
+ last4,
245
+ routing_number,
246
+ status,
247
+ name,
248
+ email,
249
+ },
250
+ paymentMethodId: newPaymentMethodId,
251
+ ...existingSubscriptionsObj,
252
+ },
253
+ stripeCustomerId:
254
+ billingObjStripeCustomerId || accountObjStripeCustomerId,
255
+ };
256
+ };
257
+
258
+ const accountUpdateObj =
259
+ type === "card"
260
+ ? generateCardBillingAccountObj()
261
+ : generateBankBillingAccountObj();
262
+
263
+ return await updateAccountBilling(accountUpdateObj);
264
+ };
265
+
266
+ const stripeSubmit = async () => {
267
+ if (!stripe || !elements) {
268
+ return;
269
+ }
270
+
271
+ const paymentMethod = await createStripePaymentMethod();
272
+
273
+ return await savePaymentMethod(paymentMethod);
274
+ };
275
+
276
+ return {
277
+ input: <StripeInputForm setCardData={setPaymentData} error={stripeError} />,
278
+ button: (
279
+ <StripeSubmitButton
280
+ stripeSubmit={stripeSubmit}
281
+ stripeError={stripeError}
282
+ isStripeComplete={isStripeComplete}
283
+ buttonText={buttonText}
284
+ />
285
+ ),
286
+ };
287
+ };
288
+ export default useStripeInput;
@@ -0,0 +1,139 @@
1
+ import React, { useEffect, useState } from "react";
2
+
3
+ import { LabelList } from "recharts";
4
+
5
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
6
+
7
+ import { Avatar } from "@mui/material";
8
+
9
+ //WL 10-20-23: This empty label with a useEffect is a super weird hack, but it's the only way I could figure out how to draw status labels
10
+ const WarningLabelSetter = ({ value, y, x, setWarningLabels, id, width }) => {
11
+ useEffect(() => {
12
+ const setWarningLabelValues = () => {
13
+ if (value) {
14
+ const { color, icon } = value;
15
+ return setWarningLabels((existingLabels) => {
16
+ const otherLabels = existingLabels.filter(
17
+ (label) => label?.id !== id
18
+ );
19
+ return [...otherLabels, { id, x, y, color, icon, width }];
20
+ });
21
+ }
22
+
23
+ return setWarningLabels((existingLabels) =>
24
+ existingLabels.filter((label) => label?.id !== id)
25
+ );
26
+ };
27
+
28
+ setWarningLabelValues();
29
+ }, [value, x, y, id, setWarningLabels, width]);
30
+
31
+ return <></>;
32
+ };
33
+
34
+ const WarningLabel = ({ x, y, icon, color, width, value, maxChartValue }) => {
35
+ const AVATAR_WIDTH = 40;
36
+
37
+ const buildCommonProps = (topOffset) => ({
38
+ backgroundColor: color,
39
+ position: "absolute",
40
+ top:
41
+ maxChartValue * 0.9 > value ? y - topOffset * 1.25 : y - AVATAR_WIDTH / 2,
42
+ });
43
+
44
+ const buildCenteringProps = (warningWidth) => ({
45
+ left: x + width / 2,
46
+ transform: `translateX(-${warningWidth / 2}px)`,
47
+ });
48
+
49
+ if (width >= AVATAR_WIDTH) {
50
+ return (
51
+ <Avatar
52
+ style={{
53
+ ...buildCommonProps(AVATAR_WIDTH),
54
+ ...buildCenteringProps(AVATAR_WIDTH),
55
+ }}
56
+ >
57
+ <FontAwesomeIcon icon={icon} style={{ color: "white" }} />
58
+ </Avatar>
59
+ );
60
+ }
61
+
62
+ const BADGE_WIDTH = 10;
63
+
64
+ const buildBadgeProps = (badgeWidth) => ({
65
+ width: badgeWidth,
66
+ height: badgeWidth,
67
+ borderRadius: "50%",
68
+ });
69
+
70
+ if (width >= BADGE_WIDTH) {
71
+ return (
72
+ <div
73
+ style={{
74
+ ...buildCommonProps(BADGE_WIDTH),
75
+ ...buildCenteringProps(BADGE_WIDTH),
76
+ ...buildBadgeProps(BADGE_WIDTH),
77
+ }}
78
+ />
79
+ );
80
+ }
81
+
82
+ return (
83
+ <div
84
+ style={{
85
+ ...buildCommonProps(width),
86
+ left: x,
87
+ ...buildBadgeProps(width),
88
+ }}
89
+ />
90
+ );
91
+ };
92
+
93
+ const useChartWarningLabels = ({ data, warningField, barSumField }) => {
94
+ const [warningLabels, setWarningLabels] = useState([]);
95
+
96
+ useEffect(() => {
97
+ if (!data.some((dataObj) => dataObj[warningField])) {
98
+ setWarningLabels([]);
99
+ }
100
+
101
+ setWarningLabels((currentLabels) =>
102
+ currentLabels.filter(({ id }) =>
103
+ data.find((dataObj) => dataObj.id === id)
104
+ )
105
+ );
106
+ }, [data, warningField]);
107
+
108
+ const maxChartValue = Math.max(
109
+ ...data.map((dataObj) => dataObj[barSumField])
110
+ );
111
+
112
+ return {
113
+ labelSetter: (
114
+ <>
115
+ <LabelList
116
+ position="insideTopLeft"
117
+ dataKey={warningField}
118
+ content={<WarningLabelSetter setWarningLabels={setWarningLabels} />}
119
+ />
120
+ </>
121
+ ),
122
+ warningLabels: (
123
+ <>
124
+ {!!warningLabels.length &&
125
+ warningLabels.map((warning, idx) => (
126
+ <WarningLabel
127
+ key={`chart-warning-label-${idx}`}
128
+ maxChartValue={maxChartValue}
129
+ value={
130
+ data.find((dataObj) => dataObj.id === warning.id)?.[barSumField]
131
+ }
132
+ {...warning}
133
+ />
134
+ ))}
135
+ </>
136
+ ),
137
+ };
138
+ };
139
+ export default useChartWarningLabels;
package/src/index.js ADDED
@@ -0,0 +1,35 @@
1
+ import FootprintVideo from "./components/FootprintVideo";
2
+ import IndustryAutocomplete from "./components/IndustryAutocomplete";
3
+ import PlacesAutocomplete from "./components/PlacesAutocomplete";
4
+ import YesNoQuestion from "./components/YesNoQuestion";
5
+ import FootprintEquivalencies from "./components/FootprintEquivalencies";
6
+ import ComparisonChart from "./components/ComparisonChart";
7
+ import CompaniesAutocomplete from "./components/CompaniesAutocomplete";
8
+ import useStripeInput from "./components/stripeInput";
9
+ import StripeElements from "./components/StripeElements";
10
+ import EmissionsReductionGraph from "./components/EmissionsReductionGraph";
11
+ import CompanyOnboardingInput from "./components/CompanyOnboardingInput";
12
+ import CustomTooltipDisplayRow from "./components/CustomTooltipDisplayRow";
13
+ import EmissionsChart from "./components/EmissionsChart";
14
+ import EmissionsCustomTooltip from "./components/EmissionsCustomTooltip";
15
+ import EmissionsPieChart from "./components/EmissionsPieChart";
16
+ import useChartWarningLabels from "./components/useChartWarningLabels";
17
+
18
+ export {
19
+ FootprintVideo,
20
+ IndustryAutocomplete,
21
+ PlacesAutocomplete,
22
+ YesNoQuestion,
23
+ FootprintEquivalencies,
24
+ ComparisonChart,
25
+ CompaniesAutocomplete,
26
+ useStripeInput,
27
+ StripeElements,
28
+ EmissionsReductionGraph,
29
+ CompanyOnboardingInput,
30
+ CustomTooltipDisplayRow,
31
+ EmissionsChart,
32
+ EmissionsCustomTooltip,
33
+ EmissionsPieChart,
34
+ useChartWarningLabels,
35
+ };