@springmicro/cart 0.7.0 → 0.7.2

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.
@@ -1,5 +1,16 @@
1
1
  import "./ReviewCartAndCalculateTaxes.css";
2
- import { Box, useTheme, useMediaQuery, Button, TextField } from "@mui/material";
2
+ import {
3
+ Box,
4
+ useTheme,
5
+ useMediaQuery,
6
+ Button,
7
+ TextField,
8
+ Modal,
9
+ Backdrop,
10
+ Fade,
11
+ Typography,
12
+ IconButton,
13
+ } from "@mui/material";
3
14
  import { setOrder as cartSetOrder } from "../utils/storage";
4
15
  import { AddCard, CCFocus, CreditCardValues } from "./components/AddCard";
5
16
  import { CartList } from "./components/CartList";
@@ -25,6 +36,8 @@ import {
25
36
  import { AddressValues } from "./components/Address";
26
37
  import { useFormik } from "formik";
27
38
  import { postCheckout } from "../utils/api";
39
+ import { CheckoutFieldsType } from ".";
40
+ import CloseIcon from "@mui/icons-material/Close";
28
41
 
29
42
  async function createOrder(cart, apiBaseUrl, dev) {
30
43
  dev && console.log("Creating order");
@@ -131,6 +144,7 @@ export default function ReviewAndCalculateTaxes({
131
144
  CollectExtraInfo = undefined,
132
145
  defaultValues = {},
133
146
  dev,
147
+ hideFields = [],
134
148
  }) {
135
149
  dev && console.log({ products, prices });
136
150
  const [formDisabled, setFormDisabled] = useState(true);
@@ -142,6 +156,8 @@ export default function ReviewAndCalculateTaxes({
142
156
  const [tax, setTax] = taxState;
143
157
  const [order, setOrder] = orderState;
144
158
 
159
+ const [precheckErrors, setPrecheckErrors] = useState([]);
160
+
145
161
  async function createOrderAndGetTaxes(values) {
146
162
  setFormError(undefined);
147
163
  setFormDisabled(true);
@@ -188,11 +204,15 @@ export default function ReviewAndCalculateTaxes({
188
204
  "stripe",
189
205
  invoiceId
190
206
  )
191
- .then((data) => {
207
+ .then(async (data) => {
192
208
  dev && console.log("Checkout submitted");
193
209
  if (data instanceof Response) {
194
210
  setFormError(true);
195
- dev && alert(data.statusText);
211
+ const { detail: errorData } = await data.json();
212
+ // dev && alert(data.statusText);
213
+ // console.log(errorData);
214
+ // TODO display issues
215
+ if (!errorData.valid) setPrecheckErrors(errorData.errors);
196
216
  } else {
197
217
  // success
198
218
  setSuccessData(data);
@@ -360,64 +380,74 @@ export default function ReviewAndCalculateTaxes({
360
380
  }, [formik.errors]);
361
381
 
362
382
  return (
363
- <Box
364
- sx={{
365
- mt: 2,
366
- display: "flex",
367
- flexDirection: { xs: "column", xl: "row" },
368
- justifyContent: "center",
369
- alignItems: { xs: undefined, xl: "flex-start" },
370
- px: { xs: 32, xl: 16 },
371
- "&>div": { width: { xs: undefined, xl: "50%" } },
372
- }}
373
- >
374
- <CartList
375
- {...{
376
- statusState: [status, setStatus],
377
- cart,
378
- subtotal,
379
- tax,
380
- shipping,
381
- discount,
382
- prices,
383
- products,
384
- disableMissingImage,
385
- disableProductLink,
386
- formik,
387
- formDisabled,
388
- formErrorState: [formError, setFormError],
383
+ <>
384
+ <Box
385
+ sx={{
386
+ mt: 2,
387
+ display: "flex",
388
+ flexDirection: { xs: "column", xl: "row" },
389
+ justifyContent: "center",
390
+ alignItems: { xs: undefined, xl: "flex-start" },
391
+ px: { xs: 32, xl: 16 },
392
+ "&>div": { width: { xs: undefined, xl: "50%" } },
389
393
  }}
390
- />
391
- {status === 0 && (
392
- <Box
393
- sx={{ flexGrow: 1, display: "flex", flexDirection: "column", gap: 2 }}
394
- >
395
- {CollectExtraInfo && <CollectExtraInfo formik={formik} />}
396
- <CheckoutActionFields
397
- formik={formik}
398
- prices={prices}
399
- products={products}
400
- />
401
- <AddCard
402
- cardRequired={cardRequired}
403
- onSubmit={createOrderAndGetTaxes}
404
- stacked={stacked}
405
- formData={{
406
- formik,
407
- handleInputChange,
408
- handleInputFocus,
409
- formDisabled,
410
- formError,
394
+ >
395
+ <CartList
396
+ {...{
397
+ statusState: [status, setStatus],
398
+ cart,
399
+ subtotal,
400
+ tax,
401
+ shipping,
402
+ discount,
403
+ prices,
404
+ products,
405
+ disableMissingImage,
406
+ disableProductLink,
407
+ formik,
408
+ formDisabled,
409
+ formErrorState: [formError, setFormError],
410
+ }}
411
+ />
412
+ {status === 0 && (
413
+ <Box
414
+ sx={{
415
+ flexGrow: 1,
416
+ display: "flex",
417
+ flexDirection: "column",
418
+ gap: 2,
411
419
  }}
412
- />
413
- </Box>
414
- )}
415
- </Box>
420
+ >
421
+ {CollectExtraInfo && <CollectExtraInfo formik={formik} />}
422
+ <CheckoutActionFields
423
+ formik={formik}
424
+ prices={prices}
425
+ products={products}
426
+ hideFields={hideFields}
427
+ />
428
+ <AddCard
429
+ cardRequired={cardRequired}
430
+ onSubmit={createOrderAndGetTaxes}
431
+ stacked={stacked}
432
+ formData={{
433
+ formik,
434
+ handleInputChange,
435
+ handleInputFocus,
436
+ formDisabled,
437
+ formError,
438
+ }}
439
+ hideFields={hideFields}
440
+ />
441
+ </Box>
442
+ )}
443
+ </Box>
444
+ <PrecheckModal errors={precheckErrors} setErrors={setPrecheckErrors} />
445
+ </>
416
446
  );
417
447
  }
418
448
 
419
- function CheckoutActionFields({ formik, products, prices }) {
420
- const checkoutFields = [
449
+ function CheckoutActionFields({ formik, products, prices, hideFields = [] }) {
450
+ const checkoutFields: Record<string, CheckoutFieldsType> = [
421
451
  ...Object.keys(products).flatMap((productKey) =>
422
452
  (products[productKey].actions ?? []).flatMap(
423
453
  (action) => action.checkout_fields ?? []
@@ -447,33 +477,109 @@ function CheckoutActionFields({ formik, products, prices }) {
447
477
  const handleInputChange = (e) => {
448
478
  formik.handleChange(e);
449
479
  };
450
-
480
+ // console.log({ checkoutFields });
451
481
  return (
452
482
  <Box sx={{ display: "flex", flexDirection: "column", mx: 3, gap: 2 }}>
453
- {Object.keys(checkoutFields).map((key) => {
454
- const {
455
- name,
456
- label,
457
- type = "string", // todo
458
- required = true, // can be a string
459
- } = checkoutFields[key];
460
-
461
- return (
462
- <TextField
463
- fullWidth
464
- name={name}
465
- label={label}
466
- required={required === true || required === "true"}
467
- onBlur={formik.handleBlur}
468
- onChange={handleInputChange}
469
- onFocus={handleInputFocus}
470
- variant="outlined"
471
- value={formik.values[name]}
472
- error={Boolean(formik.touched[name] && formik.errors[name])}
473
- helperText={formik.touched[name] && formik.errors[name]}
474
- />
475
- );
476
- })}
483
+ {Object.keys((console.log(checkoutFields), checkoutFields))
484
+ .filter(
485
+ (key) =>
486
+ !["email", ...hideFields].includes(key) &&
487
+ !(
488
+ checkoutFields[key].hidden === true ||
489
+ checkoutFields[key].hidden === "true"
490
+ )
491
+ )
492
+ .map((key) => {
493
+ const {
494
+ name,
495
+ label,
496
+ type = "string", // todo
497
+ required = true, // can be a string
498
+ } = checkoutFields[key];
499
+
500
+ return (
501
+ <TextField
502
+ fullWidth
503
+ name={name}
504
+ label={label}
505
+ required={required === true || required === "true"}
506
+ onBlur={formik.handleBlur}
507
+ onChange={handleInputChange}
508
+ onFocus={handleInputFocus}
509
+ variant="outlined"
510
+ value={formik.values[name]}
511
+ error={Boolean(formik.touched[name] && formik.errors[name])}
512
+ helperText={formik.touched[name] && formik.errors[name]}
513
+ />
514
+ );
515
+ })}
477
516
  </Box>
478
517
  );
479
518
  }
519
+
520
+ function PrecheckModal({ errors, setErrors }) {
521
+ function onClose() {
522
+ setErrors([]);
523
+ }
524
+ return (
525
+ <Modal
526
+ open={errors.length > 0}
527
+ onClose={onClose} // called when backdrop clicked or escape pressed
528
+ closeAfterTransition
529
+ slots={{ backdrop: Backdrop }}
530
+ slotProps={{
531
+ backdrop: {
532
+ timeout: 300,
533
+ },
534
+ }}
535
+ aria-labelledby="centered-modal-title"
536
+ aria-describedby="centered-modal-description"
537
+ >
538
+ <Fade in={errors.length > 0}>
539
+ <Box
540
+ sx={{
541
+ position: "absolute",
542
+ top: "50%",
543
+ left: "50%",
544
+ transform: "translate(-50%, -50%)",
545
+ minWidth: 300,
546
+ maxWidth: "90vw",
547
+ bgcolor: "background.paper",
548
+ boxShadow: 24,
549
+ borderRadius: 2,
550
+ p: 3,
551
+ outline: "none",
552
+ }}
553
+ >
554
+ <Box
555
+ sx={{
556
+ display: "flex",
557
+ alignItems: "center",
558
+ justifyContent: "space-between",
559
+ mb: 1,
560
+ gap: 4,
561
+ }}
562
+ >
563
+ <Typography id="centered-modal-title" variant="h5">
564
+ Your purchase was unsuccessful.
565
+ </Typography>
566
+ <IconButton aria-label="close" onClick={onClose} size="small">
567
+ <CloseIcon fontSize="small" />
568
+ </IconButton>
569
+ </Box>
570
+
571
+ <Box id="centered-modal-description">
572
+ <Typography variant="h6" component="h2">
573
+ Errors:
574
+ </Typography>
575
+ <ul>
576
+ {errors.map((error) => (
577
+ <Typography component="li">{error}</Typography>
578
+ ))}
579
+ </ul>
580
+ </Box>
581
+ </Box>
582
+ </Fade>
583
+ </Modal>
584
+ );
585
+ }
@@ -68,6 +68,7 @@ export type AddCardProps = {
68
68
  formDisabled;
69
69
  formError;
70
70
  };
71
+ hideFields?: string[];
71
72
  };
72
73
 
73
74
  export const AddCard = ({
@@ -84,6 +85,7 @@ export const AddCard = ({
84
85
  formDisabled,
85
86
  formError,
86
87
  },
88
+ hideFields = [],
87
89
  }: AddCardProps) => {
88
90
  return (
89
91
  <Container id="paymentMethodForm">
@@ -203,11 +205,13 @@ export const AddCard = ({
203
205
  Billing Address
204
206
  </Typography>
205
207
  <AddressCountryField formik={formik} name="country" />
206
- <AddressEmailField
207
- formik={formik}
208
- name="email"
209
- sx={{ mt: 2 }}
210
- />
208
+ {!hideFields.includes("email") && (
209
+ <AddressEmailField
210
+ formik={formik}
211
+ name="email"
212
+ sx={{ mt: 2 }}
213
+ />
214
+ )}
211
215
  <AddressOrganizationNameField
212
216
  sx={{ mt: 2 }}
213
217
  formik={formik}
@@ -177,7 +177,7 @@ export function AddressEmailField({ formik, name, sx }: AddressFieldProps) {
177
177
  return (
178
178
  <>
179
179
  <TextField
180
- label="Billing email"
180
+ label="Email"
181
181
  sx={{ width: 400, display: "block", ...sx }}
182
182
  name={name as string}
183
183
  id={name as string}
@@ -138,10 +138,10 @@ export function CartList({
138
138
  alignItems: "center",
139
139
  }}
140
140
  >
141
- <Typography color="red">
141
+ {/* <Typography color="red">
142
142
  Could not confirm payment. Your card info may have been entered
143
143
  incorrectly.
144
- </Typography>
144
+ </Typography> */}
145
145
  <Button
146
146
  onClick={() => {
147
147
  setFormError(undefined);
@@ -1,67 +1,67 @@
1
- .product-card {
2
- width: 500px;
3
- /* display: flex;
4
- flex-direction: row;
5
- justify-content: space-between; */
6
- display: grid;
7
- grid-template-columns: 1fr 40px;
8
- gap: 0.5rem;
9
- border-radius: 8px;
10
- padding: 1rem;
11
- box-shadow: 0px 0px 0px 0px rgba(0, 0, 0, 0);
12
- transition: all 0.3s 0.1s;
13
- cursor: pointer;
14
- min-height: 48px;
15
- flex-shrink: 0;
16
- }
17
-
18
- .product-card:hover {
19
- box-shadow: 0px 2px 5px 2px #dfdfdfff;
20
- transition: all 0.3s 0s;
21
- }
22
-
23
- .left-section {
24
- display: flex;
25
- flex-direction: row;
26
- gap: 1rem;
27
- flex-grow: 1;
28
- }
29
-
30
- .left-section > img,
31
- .missing-image {
32
- min-height: 100%;
33
- max-height: 100px;
34
- aspect-ratio: 1;
35
- border-radius: 6px;
36
- overflow: hidden;
37
- }
38
-
39
- .missing-image {
40
- background-color: rgb(222, 222, 222);
41
- }
42
-
43
- .two-row {
44
- display: flex;
45
- flex-direction: column;
46
- }
47
-
48
- .two-row :nth-child(1) {
49
- font-size: 20px;
50
- }
51
-
52
- .two-row :nth-child(2) {
53
- font-size: 14px;
54
- }
55
-
56
- .two-row > * {
57
- margin: 0;
58
- }
59
-
60
- .remove-button {
61
- color: #ff000040 !important;
62
- transition: all 0.2s !important;
63
- }
64
-
65
- .product-card:hover .remove-button {
66
- color: red !important;
67
- }
1
+ .product-card {
2
+ width: 500px;
3
+ /* display: flex;
4
+ flex-direction: row;
5
+ justify-content: space-between; */
6
+ display: grid;
7
+ grid-template-columns: 1fr 40px;
8
+ gap: 0.5rem;
9
+ border-radius: 8px;
10
+ padding: 1rem;
11
+ box-shadow: 0px 0px 0px 0px rgba(0, 0, 0, 0);
12
+ transition: all 0.3s 0.1s;
13
+ cursor: pointer;
14
+ min-height: 48px;
15
+ flex-shrink: 0;
16
+ }
17
+
18
+ .product-card:hover {
19
+ box-shadow: 0px 2px 5px 2px #dfdfdfff;
20
+ transition: all 0.3s 0s;
21
+ }
22
+
23
+ .left-section {
24
+ display: flex;
25
+ flex-direction: row;
26
+ gap: 1rem;
27
+ flex-grow: 1;
28
+ }
29
+
30
+ .left-section > img,
31
+ .missing-image {
32
+ min-height: 100%;
33
+ max-height: 100px;
34
+ aspect-ratio: 1;
35
+ border-radius: 6px;
36
+ overflow: hidden;
37
+ }
38
+
39
+ .missing-image {
40
+ background-color: rgb(222, 222, 222);
41
+ }
42
+
43
+ .two-row {
44
+ display: flex;
45
+ flex-direction: column;
46
+ }
47
+
48
+ .two-row :nth-child(1) {
49
+ font-size: 20px;
50
+ }
51
+
52
+ .two-row :nth-child(2) {
53
+ font-size: 14px;
54
+ }
55
+
56
+ .two-row > * {
57
+ margin: 0;
58
+ }
59
+
60
+ .remove-button {
61
+ color: #ff000040 !important;
62
+ transition: all 0.2s !important;
63
+ }
64
+
65
+ .product-card:hover .remove-button {
66
+ color: red !important;
67
+ }
File without changes