mirror of
https://github.com/vercel/commerce.git
synced 2025-05-13 21:27:50 +00:00
feat: add vehicle details to cart attributes
Signed-off-by: Chloe <pinkcloudvnn@gmail.com>
This commit is contained in:
parent
a3d416a19b
commit
682f2ecc63
@ -1,7 +1,14 @@
|
|||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
import { TAGS } from 'lib/constants';
|
import { TAGS } from 'lib/constants';
|
||||||
import { addToCart, createCart, getCart, removeFromCart, updateCart } from 'lib/shopify';
|
import {
|
||||||
|
addToCart,
|
||||||
|
createCart,
|
||||||
|
getCart,
|
||||||
|
removeFromCart,
|
||||||
|
setCartAttributes,
|
||||||
|
updateCart
|
||||||
|
} from 'lib/shopify';
|
||||||
import { revalidateTag } from 'next/cache';
|
import { revalidateTag } from 'next/cache';
|
||||||
import { cookies } from 'next/headers';
|
import { cookies } from 'next/headers';
|
||||||
|
|
||||||
@ -34,6 +41,35 @@ export async function addItem(prevState: any, selectedVariantIds: Array<string>)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function setMetafields(
|
||||||
|
prevState: any,
|
||||||
|
formData: { customer_vin: string; customer_mileage: string }
|
||||||
|
) {
|
||||||
|
const cartId = cookies().get('cartId')?.value;
|
||||||
|
|
||||||
|
if (!cartId) {
|
||||||
|
return 'Missing cart ID';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await setCartAttributes(cartId, [
|
||||||
|
{
|
||||||
|
key: 'customer_vin',
|
||||||
|
value: formData.customer_vin
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'customer_mileage',
|
||||||
|
value: formData.customer_mileage
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
revalidateTag(TAGS.cart);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
return 'Error set cart attributes';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function removeItem(prevState: any, lineIds: string[]) {
|
export async function removeItem(prevState: any, lineIds: string[]) {
|
||||||
const cartId = cookies().get('cartId')?.value;
|
const cartId = cookies().get('cartId')?.value;
|
||||||
|
|
||||||
|
@ -2,18 +2,31 @@
|
|||||||
|
|
||||||
import { Dialog, DialogPanel, Transition, TransitionChild } from '@headlessui/react';
|
import { Dialog, DialogPanel, Transition, TransitionChild } from '@headlessui/react';
|
||||||
import { ShoppingCartIcon } from '@heroicons/react/24/outline';
|
import { ShoppingCartIcon } from '@heroicons/react/24/outline';
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import LoadingDots from 'components/loading-dots';
|
||||||
import Price from 'components/price';
|
import Price from 'components/price';
|
||||||
import type { Cart } from 'lib/shopify/types';
|
import type { Cart } from 'lib/shopify/types';
|
||||||
import { Fragment, useEffect, useRef, useState } from 'react';
|
import { Fragment, useEffect, useRef, useState } from 'react';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { setMetafields } from './actions';
|
||||||
import CloseCart from './close-cart';
|
import CloseCart from './close-cart';
|
||||||
import LineItem from './line-item';
|
import LineItem from './line-item';
|
||||||
import OpenCart from './open-cart';
|
import OpenCart from './open-cart';
|
||||||
|
import VehicleDetails, { VehicleFormSchema, vehicleFormSchema } from './vehicle-details';
|
||||||
|
|
||||||
export default function CartModal({ cart }: { cart: Cart | undefined }) {
|
export default function CartModal({ cart }: { cart: Cart | undefined }) {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const quantityRef = useRef(cart?.totalQuantity);
|
const quantityRef = useRef(cart?.totalQuantity);
|
||||||
const openCart = () => setIsOpen(true);
|
const openCart = () => setIsOpen(true);
|
||||||
const closeCart = () => setIsOpen(false);
|
const closeCart = () => setIsOpen(false);
|
||||||
|
const { control, handleSubmit } = useForm<VehicleFormSchema>({
|
||||||
|
resolver: zodResolver(vehicleFormSchema)
|
||||||
|
});
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [message, setMessage] = useState<string | undefined>();
|
||||||
|
const linkRef = useRef<HTMLAnchorElement | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Open cart modal when quantity changes.
|
// Open cart modal when quantity changes.
|
||||||
@ -28,6 +41,25 @@ export default function CartModal({ cart }: { cart: Cart | undefined }) {
|
|||||||
}
|
}
|
||||||
}, [isOpen, cart?.totalQuantity, quantityRef]);
|
}, [isOpen, cart?.totalQuantity, quantityRef]);
|
||||||
|
|
||||||
|
const onSubmit = async (data: VehicleFormSchema) => {
|
||||||
|
if (!cart) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const message = await setMetafields(cart.id, data);
|
||||||
|
if (message) {
|
||||||
|
setMessage(message);
|
||||||
|
} else {
|
||||||
|
linkRef.current?.click();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setMessage('Error updating vehicle details');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button aria-label="Open cart" onClick={openCart}>
|
<button aria-label="Open cart" onClick={openCart}>
|
||||||
@ -76,7 +108,9 @@ export default function CartModal({ cart }: { cart: Cart | undefined }) {
|
|||||||
return <LineItem item={item} closeCart={closeCart} key={item.id} />;
|
return <LineItem item={item} closeCart={closeCart} key={item.id} />;
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<div className="py-4 text-sm text-neutral-500 dark:text-neutral-400">
|
<div className="py-4 text-sm text-neutral-500 dark:text-neutral-400">
|
||||||
|
<VehicleDetails control={control} />
|
||||||
<div className="mb-3 flex items-center justify-between border-b border-neutral-200 pb-1 dark:border-neutral-700">
|
<div className="mb-3 flex items-center justify-between border-b border-neutral-200 pb-1 dark:border-neutral-700">
|
||||||
<p>Taxes</p>
|
<p>Taxes</p>
|
||||||
<Price
|
<Price
|
||||||
@ -98,12 +132,26 @@ export default function CartModal({ cart }: { cart: Cart | undefined }) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a href={cart.checkoutUrl} ref={linkRef} className="hidden">
|
||||||
href={cart.checkoutUrl}
|
|
||||||
className="block w-full rounded-full bg-secondary p-3 text-center text-sm font-medium text-white opacity-90 hover:opacity-100"
|
|
||||||
>
|
|
||||||
Proceed to Checkout
|
Proceed to Checkout
|
||||||
</a>
|
</a>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className={clsx(
|
||||||
|
'flex w-full flex-row items-center justify-center gap-2 rounded-full bg-secondary p-3 text-sm font-medium text-white',
|
||||||
|
{ 'cursor-not-allowed opacity-60 hover:opacity-60': loading },
|
||||||
|
{ 'cursor-pointer opacity-90 hover:opacity-100': !loading }
|
||||||
|
)}
|
||||||
|
aria-disabled={loading}
|
||||||
|
>
|
||||||
|
{loading && <LoadingDots className="bg-white" />}
|
||||||
|
Proceed to Checkout
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<p aria-live="polite" className="sr-only" role="status">
|
||||||
|
{message}
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</DialogPanel>
|
</DialogPanel>
|
||||||
|
60
components/cart/vehicle-details.tsx
Normal file
60
components/cart/vehicle-details.tsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { Description, Field, Input, Label } from '@headlessui/react';
|
||||||
|
import { Control, Controller } from 'react-hook-form';
|
||||||
|
import * as zod from 'zod';
|
||||||
|
|
||||||
|
export const vehicleFormSchema = zod.object({
|
||||||
|
customer_vin: zod.string({ required_error: 'Vin number is required' }).min(0),
|
||||||
|
customer_mileage: zod.string({ required_error: 'Mileage is required' }).min(0)
|
||||||
|
});
|
||||||
|
|
||||||
|
export type VehicleFormSchema = zod.infer<typeof vehicleFormSchema>;
|
||||||
|
|
||||||
|
type VehicleDetailsProps = {
|
||||||
|
control: Control<VehicleFormSchema>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const VehicleDetails = ({ control }: VehicleDetailsProps) => {
|
||||||
|
return (
|
||||||
|
<div className="mb-5 mt-3 border-y border-gray-300 pb-5 pt-3">
|
||||||
|
<div className="text-base font-medium text-gray-900">Vehicle Details</div>
|
||||||
|
<Controller
|
||||||
|
name="customer_vin"
|
||||||
|
control={control}
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<Field className="mt-4">
|
||||||
|
<Label className="block text-sm font-medium text-gray-700">Vin Number</Label>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
className="mt-1 block w-full rounded-md border-gray-300 text-dark shadow-sm focus:outline-none data-[focus]:border-primary/50 data-[focus]:ring-primary/50 sm:text-sm"
|
||||||
|
autoFocus
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
{error && (
|
||||||
|
<Description className="mt-1 text-sm text-red-500">{error.message}</Description>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Controller
|
||||||
|
name="customer_mileage"
|
||||||
|
control={control}
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<Field className="mt-4">
|
||||||
|
<Label className="block text-sm font-medium text-gray-700">Current Mileage</Label>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
className="mt-1 block w-full rounded-md border-gray-300 text-dark shadow-sm focus:outline-none data-[focus]:border-primary/50 data-[focus]:ring-primary/50 sm:text-sm"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
{error && (
|
||||||
|
<Description className="mt-1 text-sm text-red-500">{error.message}</Description>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VehicleDetails;
|
@ -19,7 +19,8 @@ import {
|
|||||||
addToCartMutation,
|
addToCartMutation,
|
||||||
createCartMutation,
|
createCartMutation,
|
||||||
editCartItemsMutation,
|
editCartItemsMutation,
|
||||||
removeFromCartMutation
|
removeFromCartMutation,
|
||||||
|
setCartAttributesMutation
|
||||||
} from './mutations/cart';
|
} from './mutations/cart';
|
||||||
import { getCartQuery } from './queries/cart';
|
import { getCartQuery } from './queries/cart';
|
||||||
import {
|
import {
|
||||||
@ -39,6 +40,7 @@ import {
|
|||||||
} from './queries/product';
|
} from './queries/product';
|
||||||
import {
|
import {
|
||||||
Cart,
|
Cart,
|
||||||
|
CartAttributeInput,
|
||||||
CartItem,
|
CartItem,
|
||||||
CartProductVariant,
|
CartProductVariant,
|
||||||
Collection,
|
Collection,
|
||||||
@ -75,6 +77,7 @@ import {
|
|||||||
ShopifyProductVariant,
|
ShopifyProductVariant,
|
||||||
ShopifyProductsOperation,
|
ShopifyProductsOperation,
|
||||||
ShopifyRemoveFromCartOperation,
|
ShopifyRemoveFromCartOperation,
|
||||||
|
ShopifySetCartAttributesOperation,
|
||||||
ShopifyUpdateCartOperation
|
ShopifyUpdateCartOperation
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
@ -339,6 +342,19 @@ export async function addToCart(
|
|||||||
return reshapeCart(res.body.data.cartLinesAdd.cart);
|
return reshapeCart(res.body.data.cartLinesAdd.cart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function setCartAttributes(cartId: string, attributes: CartAttributeInput[]) {
|
||||||
|
const res = await shopifyFetch<ShopifySetCartAttributesOperation>({
|
||||||
|
query: setCartAttributesMutation,
|
||||||
|
variables: {
|
||||||
|
attributes,
|
||||||
|
cartId
|
||||||
|
},
|
||||||
|
cache: 'no-store'
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.body.data.cart;
|
||||||
|
}
|
||||||
|
|
||||||
export async function removeFromCart(cartId: string, lineIds: string[]): Promise<Cart> {
|
export async function removeFromCart(cartId: string, lineIds: string[]): Promise<Cart> {
|
||||||
const res = await shopifyFetch<ShopifyRemoveFromCartOperation>({
|
const res = await shopifyFetch<ShopifyRemoveFromCartOperation>({
|
||||||
query: removeFromCartMutation,
|
query: removeFromCartMutation,
|
||||||
@ -382,7 +398,6 @@ export async function getCart(cartId: string): Promise<Cart | undefined> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cart = reshapeCart(res.body.data.cart);
|
const cart = reshapeCart(res.body.data.cart);
|
||||||
|
|
||||||
let extendedCartLines = cart.lines;
|
let extendedCartLines = cart.lines;
|
||||||
|
|
||||||
const lineIdMap = {} as { [key: string]: string };
|
const lineIdMap = {} as { [key: string]: string };
|
||||||
|
@ -11,6 +11,17 @@ export const addToCartMutation = /* GraphQL */ `
|
|||||||
${cartFragment}
|
${cartFragment}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const setCartAttributesMutation = /* GraphQL */ `
|
||||||
|
mutation setCartAttributes($attributes: [AttributeInput!]!, $cartId: ID!) {
|
||||||
|
cartAttributesUpdate(cartId: $cartId, attributes: $attributes) {
|
||||||
|
cart {
|
||||||
|
...cart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${cartFragment}
|
||||||
|
`;
|
||||||
|
|
||||||
export const createCartMutation = /* GraphQL */ `
|
export const createCartMutation = /* GraphQL */ `
|
||||||
mutation createCart($lineItems: [CartLineInput!]) {
|
mutation createCart($lineItems: [CartLineInput!]) {
|
||||||
cartCreate(input: { lines: $lineItems }) {
|
cartCreate(input: { lines: $lineItems }) {
|
||||||
|
@ -242,6 +242,16 @@ export type ShopifyAddToCartOperation = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ShopifySetCartAttributesOperation = {
|
||||||
|
data: {
|
||||||
|
cart: ShopifyCart;
|
||||||
|
};
|
||||||
|
variables: {
|
||||||
|
attributes: CartAttributeInput[];
|
||||||
|
cartId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export type ShopifyRemoveFromCartOperation = {
|
export type ShopifyRemoveFromCartOperation = {
|
||||||
data: {
|
data: {
|
||||||
cartLinesRemove: {
|
cartLinesRemove: {
|
||||||
@ -428,3 +438,8 @@ export type Filter = {
|
|||||||
export const SCREEN_SIZES = ['small', 'medium', 'large', 'extra_large'] as const;
|
export const SCREEN_SIZES = ['small', 'medium', 'large', 'extra_large'] as const;
|
||||||
|
|
||||||
export type ScreenSize = (typeof SCREEN_SIZES)[number];
|
export type ScreenSize = (typeof SCREEN_SIZES)[number];
|
||||||
|
|
||||||
|
export type CartAttributeInput = {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^2.0.1",
|
"@headlessui/react": "^2.0.1",
|
||||||
"@heroicons/react": "^2.1.3",
|
"@heroicons/react": "^2.1.3",
|
||||||
|
"@hookform/resolvers": "^3.6.0",
|
||||||
"@radix-ui/react-checkbox": "^1.0.4",
|
"@radix-ui/react-checkbox": "^1.0.4",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"geist": "^1.3.0",
|
"geist": "^1.3.0",
|
||||||
@ -33,8 +34,10 @@
|
|||||||
"next": "14.1.4",
|
"next": "14.1.4",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"react-hook-form": "^7.51.5",
|
||||||
"react-tooltip": "^5.26.3",
|
"react-tooltip": "^5.26.3",
|
||||||
"tailwind-merge": "^2.2.2"
|
"tailwind-merge": "^2.2.2",
|
||||||
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||||
|
30
pnpm-lock.yaml
generated
30
pnpm-lock.yaml
generated
@ -11,6 +11,9 @@ dependencies:
|
|||||||
'@heroicons/react':
|
'@heroicons/react':
|
||||||
specifier: ^2.1.3
|
specifier: ^2.1.3
|
||||||
version: 2.1.3(react@18.2.0)
|
version: 2.1.3(react@18.2.0)
|
||||||
|
'@hookform/resolvers':
|
||||||
|
specifier: ^3.6.0
|
||||||
|
version: 3.6.0(react-hook-form@7.51.5)
|
||||||
'@radix-ui/react-checkbox':
|
'@radix-ui/react-checkbox':
|
||||||
specifier: ^1.0.4
|
specifier: ^1.0.4
|
||||||
version: 1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.72)(react-dom@18.2.0)(react@18.2.0)
|
version: 1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.72)(react-dom@18.2.0)(react@18.2.0)
|
||||||
@ -38,12 +41,18 @@ dependencies:
|
|||||||
react-dom:
|
react-dom:
|
||||||
specifier: 18.2.0
|
specifier: 18.2.0
|
||||||
version: 18.2.0(react@18.2.0)
|
version: 18.2.0(react@18.2.0)
|
||||||
|
react-hook-form:
|
||||||
|
specifier: ^7.51.5
|
||||||
|
version: 7.51.5(react@18.2.0)
|
||||||
react-tooltip:
|
react-tooltip:
|
||||||
specifier: ^5.26.3
|
specifier: ^5.26.3
|
||||||
version: 5.26.3(react-dom@18.2.0)(react@18.2.0)
|
version: 5.26.3(react-dom@18.2.0)(react@18.2.0)
|
||||||
tailwind-merge:
|
tailwind-merge:
|
||||||
specifier: ^2.2.2
|
specifier: ^2.2.2
|
||||||
version: 2.2.2
|
version: 2.2.2
|
||||||
|
zod:
|
||||||
|
specifier: ^3.23.8
|
||||||
|
version: 3.23.8
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@tailwindcss/aspect-ratio':
|
'@tailwindcss/aspect-ratio':
|
||||||
@ -255,6 +264,14 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@hookform/resolvers@3.6.0(react-hook-form@7.51.5):
|
||||||
|
resolution: {integrity: sha512-UBcpyOX3+RR+dNnqBd0lchXpoL8p4xC21XP8H6Meb8uve5Br1GCnmg0PcBoKKqPKgGu9GHQ/oygcmPrQhetwqw==}
|
||||||
|
peerDependencies:
|
||||||
|
react-hook-form: ^7.0.0
|
||||||
|
dependencies:
|
||||||
|
react-hook-form: 7.51.5(react@18.2.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@humanwhocodes/config-array@0.11.14:
|
/@humanwhocodes/config-array@0.11.14:
|
||||||
resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
|
resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
|
||||||
engines: {node: '>=10.10.0'}
|
engines: {node: '>=10.10.0'}
|
||||||
@ -3254,6 +3271,15 @@ packages:
|
|||||||
scheduler: 0.23.0
|
scheduler: 0.23.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/react-hook-form@7.51.5(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-J2ILT5gWx1XUIJRETiA7M19iXHlG74+6O3KApzvqB/w8S5NQR7AbU8HVZrMALdmDgWpRPYiZJl0zx8Z4L2mP6Q==}
|
||||||
|
engines: {node: '>=12.22.0'}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.8.0 || ^17 || ^18
|
||||||
|
dependencies:
|
||||||
|
react: 18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/react-is@16.13.1:
|
/react-is@16.13.1:
|
||||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -4031,3 +4057,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/zod@3.23.8:
|
||||||
|
resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==}
|
||||||
|
dev: false
|
||||||
|
Loading…
x
Reference in New Issue
Block a user