From bf2171a3c902f28dc6b7ae2a56f741578465b385 Mon Sep 17 00:00:00 2001 From: dopoto Date: Mon, 12 May 2025 09:03:42 +0300 Subject: [PATCH 01/47] pay --- src/components/public/PublicMenuItem.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/public/PublicMenuItem.tsx b/src/components/public/PublicMenuItem.tsx index cad5ca0..68e3b7b 100644 --- a/src/components/public/PublicMenuItem.tsx +++ b/src/components/public/PublicMenuItem.tsx @@ -1,5 +1,6 @@ import { SoupIcon, WineIcon } from 'lucide-react'; import { Badge } from '~/components/ui/badge'; +import { Button } from '~/components/ui/button'; import { CURRENCIES, type CurrencyId } from '~/domain/currencies'; import { type MenuItem } from '~/domain/menu-items'; @@ -22,7 +23,10 @@ export function PublicMenuItem(props: { item: Partial; currencyId: Cur
{description}
- {price} {currency.symbol} + {price} {currency.symbol}{' '} +
From 4758d1765d87477443a0670eee4b4a0ca4aa94cf Mon Sep 17 00:00:00 2001 From: dopoto Date: Mon, 12 May 2025 13:46:26 +0300 Subject: [PATCH 02/47] wip paymentIntent created --- package.json | 4 +- .../actions/createMenuItemPaymentIntent.ts | 45 +++++ src/app/api/webhooks/stripe/route.ts | 176 ++++++++++++++++++ .../[locationSlug]/_components/PublicMenu.tsx | 9 +- src/components/public/PaymentButton.tsx | 121 ++++++++++++ src/components/public/PublicMenuItem.tsx | 61 +++++- src/domain/payments.ts | 70 +++++++ src/lib/payment-utils.ts | 172 +++++++++++++++++ 8 files changed, 649 insertions(+), 9 deletions(-) create mode 100644 src/app/actions/createMenuItemPaymentIntent.ts create mode 100644 src/app/api/webhooks/stripe/route.ts create mode 100644 src/components/public/PaymentButton.tsx create mode 100644 src/domain/payments.ts create mode 100644 src/lib/payment-utils.ts diff --git a/package.json b/package.json index 1eae83f..aa39524 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next-menu", - "version": "0.0.68", + "version": "0.0.69", "private": true, "type": "module", "scripts": { @@ -78,7 +78,7 @@ "react-use": "17.6.0", "server-only": "^0.0.1", "stripe": "^18.1.0", - "tailwind-merge": "^3.2.0", + "tailwind-merge": "^3.3.0", "tailwindcss-animate": "^1.0.7", "truncate-middle": "^2.0.1", "zod": "3.24.4" diff --git a/src/app/actions/createMenuItemPaymentIntent.ts b/src/app/actions/createMenuItemPaymentIntent.ts new file mode 100644 index 0000000..5c59096 --- /dev/null +++ b/src/app/actions/createMenuItemPaymentIntent.ts @@ -0,0 +1,45 @@ +'use server'; + +import { type MenuItem } from '~/domain/menu-items'; +import { type PaymentIntentResponse } from '~/domain/payments'; +import { createPaymentIntent, verifyConnectAccount } from '~/lib/payment-utils'; + +export async function createMenuItemPaymentIntent( + item: MenuItem, + merchantStripeAccountId: string, +): Promise { + try { + if (!item || !item.price || !item.name) { + throw new Error('Invalid menu item data'); + } + if (!merchantStripeAccountId || !merchantStripeAccountId.startsWith('acct_')) { + throw new Error('Invalid merchant Stripe account'); + } + + // Verify that the Connect account is properly set up + const isValidAccount = await verifyConnectAccount(merchantStripeAccountId); + if (!isValidAccount) { + throw new Error('Merchant account is not fully set up for payments yet'); + } + + const paymentIntent = await createPaymentIntent(item, merchantStripeAccountId); + + if (!paymentIntent.client_secret) { + throw new Error('No client secret received from Stripe'); + } + + return { + clientSecret: paymentIntent.client_secret, + paymentIntentId: paymentIntent.id, + }; + } catch (error) { + console.error('Failed to create payment intent:', error); + if (error instanceof Error) { + if (error.message.includes('Stripe')) { + throw new Error('Payment service temporarily unavailable. Please try again.'); + } + throw new Error(error.message); + } + throw new Error('Failed to initiate payment. Please try again.'); + } +} diff --git a/src/app/api/webhooks/stripe/route.ts b/src/app/api/webhooks/stripe/route.ts new file mode 100644 index 0000000..56c6e01 --- /dev/null +++ b/src/app/api/webhooks/stripe/route.ts @@ -0,0 +1,176 @@ +'use server'; + +import { headers } from 'next/headers'; +import Stripe from 'stripe'; +import { env } from '~/env'; +import { AppError } from '~/lib/error-utils.server'; +import { processFailedPayment, processSuccessfulPayment } from '~/lib/payment-utils'; + +const stripe = new Stripe(env.STRIPE_SECRET_KEY); +const webhookSecret = env.STRIPE_WEBHOOK_SECRET; + +export async function POST(request: Request) { + const body = await request.text(); + const headersList = headers(); + const signature = headersList.get('stripe-signature'); + + if (!signature) { + console.error('Missing stripe-signature header'); + return new Response('Missing stripe-signature', { status: 400 }); + } + + let event: Stripe.Event; + + try { + event = stripe.webhooks.constructEvent(body, signature, webhookSecret); + } catch (err) { + console.error('Error verifying webhook signature:', err); + return new Response( + `Webhook signature verification failed: ${err instanceof Error ? err.message : 'Unknown error'}`, + { + status: 400, + }, + ); + } + + try { + switch (event.type) { + case 'payment_intent.succeeded': + await handlePaymentIntentSucceeded(event.data.object as Stripe.PaymentIntent); + break; + case 'payment_intent.payment_failed': + await handlePaymentIntentFailed(event.data.object as Stripe.PaymentIntent); + break; + case 'charge.refunded': + await handleChargeRefunded(event.data.object as Stripe.Charge); + break; + case 'payment_intent.requires_action': + await handlePaymentIntentRequiresAction(event.data.object as Stripe.PaymentIntent); + break; + default: + console.log(`Unhandled event type: ${event.type}`); + } + return new Response('Webhook processed successfully', { status: 200 }); + } catch (err) { + console.error('Error processing webhook:', err); + return new Response(`Webhook processing failed: ${err instanceof Error ? err.message : 'Unknown error'}`, { + status: 400, + }); + } +} + +async function handlePaymentIntentSucceeded(paymentIntent: Stripe.PaymentIntent) { + try { + const customer = paymentIntent.customer as string | null; + + const subscription = await findSubscriptionByPaymentIntent(paymentIntent.id); + if (subscription) { + // For subscription payments + if (!customer) { + throw new AppError({ + internalMessage: `No customer found for subscription payment intent ${paymentIntent.id}`, + }); + } + await handleSubscriptionPayment(customer, subscription); + } else { + // For one-time payments (like menu items), process merchant transfer + await processSuccessfulPayment(paymentIntent); + + // If this is a menu item payment, handle specific logic + if (paymentIntent.metadata?.menuItemId) { + // Add your menu item purchase handling logic here + // For example: update order status, send confirmation, etc. + console.log(`Menu item ${paymentIntent.metadata.menuItemId} purchased successfully`); + } + } + } catch (err) { + console.error('Error handling successful payment:', err); + throw err; + } +} + +async function handlePaymentIntentFailed(paymentIntent: Stripe.PaymentIntent) { + try { + await processFailedPayment(paymentIntent); + + if (paymentIntent.metadata?.menuItemId) { + console.log(`Menu item payment failed: ${paymentIntent.metadata.menuItemId}`); + // Add your failed menu item purchase handling logic here + // For example: notify merchant, update order status, etc. + } + } catch (err) { + console.error('Error handling failed payment:', err); + throw err; + } +} + +async function handleChargeRefunded(charge: Stripe.Charge) { + try { + // Get the original payment intent if it exists + const paymentIntentId = typeof charge.payment_intent === 'string' ? charge.payment_intent : null; + + if (paymentIntentId) { + const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentId); + + // If this was a menu item payment, handle it specially + if (paymentIntent.metadata?.menuItemId) { + console.log(`Menu item ${paymentIntent.metadata.menuItemId} refunded`); + // Add your menu item refund handling logic here + // For example: update order status, notify merchant, etc. + } + } + + console.log(`Refund processed for charge ${charge.id}`); + } catch (err) { + console.error('Error handling refund:', err); + throw err; + } +} + +async function handlePaymentIntentRequiresAction(paymentIntent: Stripe.PaymentIntent) { + try { + if (paymentIntent.metadata?.menuItemId) { + console.log(`Menu item ${paymentIntent.metadata.menuItemId} requires additional authentication`); + // Handle any special logic needed for 3D Secure, etc. + } + } catch (err) { + console.error('Error handling payment requires action:', err); + throw err; + } +} + +async function handleSubscriptionPayment(stripeCustomerId: string, subscription: Stripe.Subscription) { + // Update subscription status in database + console.log(`Subscription payment successful for customer ${stripeCustomerId}`); + + // You could add additional subscription-specific logic here + // For example: + // - Update subscription status in your database + // - Send confirmation emails + // - Update user's subscription tier + // - Track subscription metrics +} + +async function findSubscriptionByPaymentIntent(paymentIntentId: string): Promise { + try { + // First try to find the payment intent to get the customer + const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentId); + const customerId = paymentIntent.customer as string | null; + + if (!customerId) { + return null; + } + + // Then look for an active subscription for this customer + const subscriptions = await stripe.subscriptions.list({ + customer: customerId, + status: 'active', + limit: 1, + }); + + return subscriptions.data[0] || null; + } catch (err) { + console.error('Error finding subscription:', err); + return null; + } +} diff --git a/src/app/p/[locationSlug]/_components/PublicMenu.tsx b/src/app/p/[locationSlug]/_components/PublicMenu.tsx index 38508c0..ef3cb67 100644 --- a/src/app/p/[locationSlug]/_components/PublicMenu.tsx +++ b/src/app/p/[locationSlug]/_components/PublicMenu.tsx @@ -7,7 +7,14 @@ export async function PublicMenu(props: { name: string; currencyId: CurrencyId; <>

{props.name}

{props.items?.map((item) => { - return ; + return ( + + ); })} ); diff --git a/src/components/public/PaymentButton.tsx b/src/components/public/PaymentButton.tsx new file mode 100644 index 0000000..fe37229 --- /dev/null +++ b/src/components/public/PaymentButton.tsx @@ -0,0 +1,121 @@ +'use client'; + +import { Elements, useElements, useStripe } from '@stripe/react-stripe-js'; +import { loadStripe, type PaymentRequest } from '@stripe/stripe-js'; +import { useEffect, useState } from 'react'; +import { Button } from '~/components/ui/button'; +import { env } from '~/env'; + +const stripePromise = loadStripe(env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY, { + betas: ['payment_element_beta_1'], +}); + +export interface PaymentButtonProps { + clientSecret: string; + merchantName: string; + amount: number; + onSuccess: () => void; + onError: (error: Error) => void; +} + +function PaymentRequestButton({ clientSecret, merchantName, amount, onSuccess, onError }: PaymentButtonProps) { + const stripe = useStripe(); + const elements = useElements(); + const [paymentRequest, setPaymentRequest] = useState(null); + const [isProcessing, setIsProcessing] = useState(false); + useEffect(() => { + if (!stripe || !elements) return; + + const amountInCents = Math.round(amount * 100); + const pr = stripe.paymentRequest({ + country: 'US', + currency: 'usd', + total: { + label: merchantName, + amount: amountInCents, + pending: false, + }, + requestPayerName: true, + requestPayerEmail: true, + }); + + pr.canMakePayment().then((result) => { + if (result) { + setPaymentRequest(pr); + } + }); + pr.on('paymentmethod', async (e) => { + try { + setIsProcessing(true); + if (!stripe) { + throw new Error('Stripe not initialized'); + } + const { error, paymentIntent } = await stripe.confirmCardPayment( + clientSecret, + { + payment_method: e.paymentMethod.id, + }, + { handleActions: false }, + ); + + if (error) { + e.complete('fail'); + onError(new Error(error.message)); + return; + } + + e.complete('success'); + if (paymentIntent.status === 'requires_action') { + if (!stripe) { + throw new Error('Stripe not initialized'); + } + const { error: confirmError } = await stripe.confirmCardPayment(clientSecret); + if (confirmError) { + onError(new Error(confirmError.message)); + return; + } + } + + onSuccess(); + } catch (err) { + e.complete('fail'); + onError(err instanceof Error ? err : new Error('Payment failed')); + } finally { + setIsProcessing(false); + } + }); + return () => { + // Clean up event listeners if needed + pr.off('paymentmethod'); + }; + }, [stripe, elements, clientSecret, merchantName, amount, onSuccess, onError]); + + if (!paymentRequest) { + return null; + } + + return ( + + ); +} + +export function PaymentButton(props: PaymentButtonProps) { + return ( + + + + ); +} diff --git a/src/components/public/PublicMenuItem.tsx b/src/components/public/PublicMenuItem.tsx index 68e3b7b..9aa3d7a 100644 --- a/src/components/public/PublicMenuItem.tsx +++ b/src/components/public/PublicMenuItem.tsx @@ -1,12 +1,21 @@ -import { SoupIcon, WineIcon } from 'lucide-react'; +'use client'; + +import { LoaderIcon, SoupIcon, WineIcon } from 'lucide-react'; +import { useState } from 'react'; +import { createMenuItemPaymentIntent } from '~/app/actions/createMenuItemPaymentIntent'; import { Badge } from '~/components/ui/badge'; import { Button } from '~/components/ui/button'; import { CURRENCIES, type CurrencyId } from '~/domain/currencies'; import { type MenuItem } from '~/domain/menu-items'; +import { type PaymentIntentResponse } from '~/domain/payments'; +import { PaymentButton } from './PaymentButton'; -export function PublicMenuItem(props: { item: Partial; currencyId: CurrencyId }) { +export function PublicMenuItem(props: { item: MenuItem; currencyId: CurrencyId; merchantStripeAccountId: string }) { const { name, description, price, isNew, type } = props.item; const currency = CURRENCIES[props.currencyId]; + const [paymentIntent, setPaymentIntent] = useState(null); + const [error, setError] = useState(null); + const [isLoading, setIsLoading] = useState(false); return (
@@ -21,13 +30,53 @@ export function PublicMenuItem(props: { item: Partial; currencyId: Cur )}
-
{description}
+
{description}
{' '}
{price} {currency.symbol}{' '} - + {paymentIntent ? ( + { + setPaymentIntent(null); + setError(null); + }} + onError={(err) => setError(err.message)} + /> + ) : ( + + )}
+ {error &&
{error}
}
); diff --git a/src/domain/payments.ts b/src/domain/payments.ts new file mode 100644 index 0000000..5375af8 --- /dev/null +++ b/src/domain/payments.ts @@ -0,0 +1,70 @@ +import { type MenuItem } from '~/domain/menu-items'; + +export interface PaymentIntent { + id: string; + amount: number; + currency: string; + status: PaymentStatus; + clientSecret: string; + metadata?: { + menuItemId?: string; + menuItemName?: string; + type?: MenuItem['type']; + }; +} + +export type PaymentStatus = + | 'requires_payment_method' // Initial status + | 'requires_confirmation' // After payment method attached + | 'requires_action' // 3D Secure required + | 'processing' // Payment is being processed + | 'requires_capture' // For authorize-only flows + | 'succeeded' // Payment completed successfully + | 'failed'; // Payment failed + +// Response when creating a payment intent +export interface PaymentIntentResponse { + clientSecret: string; + paymentIntentId: string; +} + +export interface PaymentErrorResponse { + code: string; + message: string; + paymentIntentId?: string; +} + +export interface PaymentButtonProps { + clientSecret: string; + merchantName: string; + amount: number; + onSuccess: () => void; + onError: (error: Error) => void; +} + +// Webhook event types we handle +export type PaymentWebhookEvent = + | 'payment_intent.succeeded' + | 'payment_intent.payment_failed' + | 'charge.refunded' + | 'payment_intent.requires_action'; + +// Payment transfer details +export interface PaymentTransfer { + id: string; + amount: number; + currency: string; + destinationAccountId: string; + paymentIntentId: string; + metadata?: Record; +} + +// Payment refund details +export interface PaymentRefund { + id: string; + amount: number; + currency: string; + paymentIntentId: string; + reason?: string; + status: 'succeeded' | 'failed' | 'pending'; +} diff --git a/src/lib/payment-utils.ts b/src/lib/payment-utils.ts new file mode 100644 index 0000000..d3927d8 --- /dev/null +++ b/src/lib/payment-utils.ts @@ -0,0 +1,172 @@ +import Stripe from 'stripe'; +import { type MenuItem } from '~/domain/menu-items'; +import { env } from '~/env'; + +const stripe = new Stripe(env.STRIPE_SECRET_KEY); + +// Calculate the amount that goes to the app (0.5%) +export function calculateAppFee(amount: number): number { + return Math.round(amount * 0.005); // 0.5% +} + +// Format price for Stripe (convert to cents) +export function formatStripeAmount(price: number): number { + return Math.round(price * 100); +} + +export async function createPaymentIntent(item: MenuItem, merchantStripeAccountId: string) { + // Convert price string to number and format for Stripe + const priceNum = parseFloat(item.price); + if (isNaN(priceNum)) { + throw new Error('Invalid price format'); + } + const amountInCents = formatStripeAmount(priceNum); + const applicationFeeAmount = calculateAppFee(amountInCents); + return stripe.paymentIntents.create( + { + amount: amountInCents, currency: 'usd', + payment_method_types: ['card'], // Start with just card payments, add more after enabling them in Stripe Dashboard + application_fee_amount: applicationFeeAmount, + metadata: { + menuItemId: item.id, + menuItemName: item.name, + type: item.type, + merchantId: merchantStripeAccountId, + }, + }, + { + stripeAccount: merchantStripeAccountId, // This tells Stripe we're creating the PaymentIntent on behalf of the connected account + }, + ); +} + +interface PaymentStatusUpdate { + paymentIntentId: string; + status: 'succeeded' | 'failed' | 'requires_payment_method' | 'requires_action'; + lastError?: string; +} + +// Validate webhook signature +export async function validateStripeWebhookSignature( + payload: string | Buffer, + signature: string, + webhookSecret: string, +): Promise { + try { + return stripe.webhooks.constructEvent(payload, signature, webhookSecret); + } catch (err) { + throw new Error( + `Webhook signature verification failed: ${err instanceof Error ? err.message : 'Unknown error'}`, + ); + } +} + +// Process a successful payment intent +export async function processSuccessfulPayment(paymentIntent: Stripe.PaymentIntent) { + if (paymentIntent.transfer_data?.destination) { + // For menu item payments, transfer funds to merchant + const amountReceived = paymentIntent.amount_received; + const appFeeAmount = calculateAppFee(amountReceived); + + await stripe.transfers.create({ + amount: amountReceived - appFeeAmount, + currency: paymentIntent.currency, + destination: paymentIntent.transfer_data.destination.toString(), + transfer_group: paymentIntent.id, + description: `Transfer for payment ${paymentIntent.id}`, + metadata: { + paymentIntentId: paymentIntent.id, + ...paymentIntent.metadata, + }, + }); + } + + // You can add more logic here, such as: + // - Updating order status in database + // - Sending confirmation emails + // - Triggering other business logic +} + +// Process a failed payment intent +export async function processFailedPayment(paymentIntent: Stripe.PaymentIntent) { + // Handle failed payment logic, such as: + // - Marking order as failed in database + // - Sending failure notification + // - Logging for analytics + console.log(`Payment failed for payment intent ${paymentIntent.id}:`, paymentIntent.last_payment_error?.message); +} + +// Get payment status details +export async function getPaymentDetails(paymentIntentId: string): Promise { + const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentId); + + return { + paymentIntentId: paymentIntent.id, + status: paymentIntent.status as PaymentStatusUpdate['status'], + lastError: paymentIntent.last_payment_error?.message, + }; +} + +interface CreateConnectAccountParams { + email: string; + country: string; + businessType: 'individual' | 'company'; + businessProfile: { + name: string; + url?: string; + mcc?: string; // Merchant Category Code (e.g., "5812" for restaurants) + }; +} + +// Create a Stripe Connect account for a merchant +export async function createConnectAccount(params: CreateConnectAccountParams) { + try { + const account = await stripe.accounts.create({ + type: 'express', + country: params.country, + email: params.email, + business_type: params.businessType, + capabilities: { + card_payments: { requested: true }, + transfers: { requested: true }, + }, + business_profile: { + name: params.businessProfile.name, + url: params.businessProfile.url, + mcc: params.businessProfile.mcc || '5812', // Default to restaurants + }, + }); + + // Create an account link for onboarding + const accountLink = await stripe.accountLinks.create({ + account: account.id, + refresh_url: `${env.NEXT_PUBLIC_APP_URL}/merchant/onboard/refresh`, + return_url: `${env.NEXT_PUBLIC_APP_URL}/merchant/onboard/complete`, + type: 'account_onboarding', + }); + + return { + accountId: account.id, + onboardingUrl: accountLink.url, + }; + } catch (error) { + console.error('Failed to create Connect account:', error); + throw new Error('Failed to set up merchant account. Please try again.'); + } +} + +// Check if a Connect account is properly set up +export async function verifyConnectAccount(accountId: string): Promise { + try { + const account = await stripe.accounts.retrieve(accountId); + return ( + account.charges_enabled && + account.payouts_enabled && + account.capabilities?.card_payments === 'active' && + account.capabilities?.transfers === 'active' + ); + } catch (error) { + console.error('Failed to verify Connect account:', error); + return false; + } +} From 205e82aeb763cf98d9ae623d19dea4e462664790 Mon Sep 17 00:00:00 2001 From: dopoto Date: Tue, 13 May 2025 10:06:57 +0300 Subject: [PATCH 03/47] wip --- src/components/public/PaymentButton.tsx | 123 +++++++++++++++--------- src/lib/payment-utils.ts | 5 +- 2 files changed, 80 insertions(+), 48 deletions(-) diff --git a/src/components/public/PaymentButton.tsx b/src/components/public/PaymentButton.tsx index fe37229..632ef03 100644 --- a/src/components/public/PaymentButton.tsx +++ b/src/components/public/PaymentButton.tsx @@ -1,14 +1,12 @@ 'use client'; -import { Elements, useElements, useStripe } from '@stripe/react-stripe-js'; -import { loadStripe, type PaymentRequest } from '@stripe/stripe-js'; +import { Elements, PaymentElement, PaymentRequestButtonElement, useElements, useStripe } from '@stripe/react-stripe-js'; +import { loadStripe, type PaymentRequest, StripeElementsOptions } from '@stripe/stripe-js'; import { useEffect, useState } from 'react'; import { Button } from '~/components/ui/button'; import { env } from '~/env'; -const stripePromise = loadStripe(env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY, { - betas: ['payment_element_beta_1'], -}); +const stripePromise = loadStripe(env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY); export interface PaymentButtonProps { clientSecret: string; @@ -22,7 +20,9 @@ function PaymentRequestButton({ clientSecret, merchantName, amount, onSuccess, o const stripe = useStripe(); const elements = useElements(); const [paymentRequest, setPaymentRequest] = useState(null); + const [showCardForm, setShowCardForm] = useState(false); const [isProcessing, setIsProcessing] = useState(false); + useEffect(() => { if (!stripe || !elements) return; @@ -39,11 +39,25 @@ function PaymentRequestButton({ clientSecret, merchantName, amount, onSuccess, o requestPayerEmail: true, }); - pr.canMakePayment().then((result) => { - if (result) { - setPaymentRequest(pr); - } - }); + pr.canMakePayment() + .then((result) => { + console.log('Payment Request capability check result:', result); + if (result) { + console.log('Available payment methods:', { + applePay: result.applePay, + googlePay: result.googlePay, + }); + setPaymentRequest(pr); + } else { + console.log('No digital wallet payment methods available'); + setShowCardForm(true); + } + }) + .catch((error) => { + console.error('Error checking payment capability:', error); + setShowCardForm(true); + }); + pr.on('paymentmethod', async (e) => { try { setIsProcessing(true); @@ -61,22 +75,10 @@ function PaymentRequestButton({ clientSecret, merchantName, amount, onSuccess, o if (error) { e.complete('fail'); onError(new Error(error.message)); - return; + } else { + e.complete('success'); + onSuccess(); } - - e.complete('success'); - if (paymentIntent.status === 'requires_action') { - if (!stripe) { - throw new Error('Stripe not initialized'); - } - const { error: confirmError } = await stripe.confirmCardPayment(clientSecret); - if (confirmError) { - onError(new Error(confirmError.message)); - return; - } - } - - onSuccess(); } catch (err) { e.complete('fail'); onError(err instanceof Error ? err : new Error('Payment failed')); @@ -84,37 +86,66 @@ function PaymentRequestButton({ clientSecret, merchantName, amount, onSuccess, o setIsProcessing(false); } }); + return () => { - // Clean up event listeners if needed - pr.off('paymentmethod'); + pr.removeAllListeners(); }; - }, [stripe, elements, clientSecret, merchantName, amount, onSuccess, onError]); + }, [stripe, elements, amount, clientSecret, merchantName, onError, onSuccess]); - if (!paymentRequest) { - return null; - } + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!stripe || !elements) return; + + setIsProcessing(true); + try { + const { error } = await stripe.confirmPayment({ + elements, + confirmParams: { + return_url: window.location.href, + }, + }); + + if (error) { + onError(new Error(error.message)); + } else { + onSuccess(); + } + } catch (err) { + onError(err instanceof Error ? err : new Error('Payment failed')); + } finally { + setIsProcessing(false); + } + }; return ( - +
+ {paymentRequest && ( +
+ +
or pay with card below
+
+ )} + + {showCardForm && ( +
+ + + + )} +
); } export function PaymentButton(props: PaymentButtonProps) { + const options: StripeElementsOptions = { + clientSecret: props.clientSecret, + appearance: { theme: 'stripe' as const }, + }; + return ( - + ); diff --git a/src/lib/payment-utils.ts b/src/lib/payment-utils.ts index d3927d8..13412b5 100644 --- a/src/lib/payment-utils.ts +++ b/src/lib/payment-utils.ts @@ -24,8 +24,9 @@ export async function createPaymentIntent(item: MenuItem, merchantStripeAccountI const applicationFeeAmount = calculateAppFee(amountInCents); return stripe.paymentIntents.create( { - amount: amountInCents, currency: 'usd', - payment_method_types: ['card'], // Start with just card payments, add more after enabling them in Stripe Dashboard + amount: amountInCents, + currency: 'usd', + payment_method_types: ['card'], // Digital wallets will be handled by the Payment Request Button application_fee_amount: applicationFeeAmount, metadata: { menuItemId: item.id, From f669f5222f79f94073f2b722284a10bc28827b2b Mon Sep 17 00:00:00 2001 From: dopoto Date: Tue, 13 May 2025 10:14:33 +0300 Subject: [PATCH 04/47] wip --- package.json | 8 +- pnpm-lock.yaml | 980 ++++++++++++------ .../_components/PublicFooter.tsx | 3 + src/app/p/[locationSlug]/layout.tsx | 3 + src/components/public/PaymentButton.tsx | 2 +- 5 files changed, 655 insertions(+), 341 deletions(-) create mode 100644 src/app/p/[locationSlug]/_components/PublicFooter.tsx diff --git a/package.json b/package.json index aa39524..f82acb4 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,8 @@ "drizzle-orm": "^0.43.1", "embla-carousel-react": "^8.6.0", "geist": "^1.4.2", - "lucide-react": "^0.509.0", + "jotai": "^2.12.4", + "lucide-react": "^0.510.0", "next": "15.3.2", "postgres": "^3.4.5", "posthog-js": "^1.240.6", @@ -99,8 +100,8 @@ "@types/eslint": "^9.6.1", "@types/jest": "^29.5.14", "@types/node": "^22.15.17", - "@types/react": "^19.1.3", - "@types/react-dom": "^19.1.3", + "@types/react": "^19.1.4", + "@types/react-dom": "^19.1.4", "cross-env": "^7.0.3", "drizzle-kit": "^0.31.1", "eslint": "9.26.0", @@ -110,6 +111,7 @@ "eslint-plugin-storybook": "^0.12.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", + "jotai-devtools": "^0.12.0", "knip": "^5.55.1", "postcss": "^8.5.3", "prettier": "^3.5.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 630d950..e8c85ff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,49 +34,49 @@ importers: version: 15.3.2(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) '@radix-ui/react-avatar': specifier: ^1.1.9 - version: 1.1.9(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-checkbox': specifier: ^1.3.1 - version: 1.3.1(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.3.1(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-collapsible': specifier: ^1.1.10 - version: 1.1.10(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.10(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-dialog': specifier: ^1.1.13 - version: 1.1.13(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.13(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-dropdown-menu': specifier: ^2.1.14 - version: 2.1.14(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.1.14(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-label': specifier: ^2.1.6 - version: 2.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-popover': specifier: ^1.1.13 - version: 1.1.13(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.13(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-progress': specifier: ^1.1.6 - version: 1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-radio-group': specifier: ^1.3.6 - version: 1.3.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.3.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-separator': specifier: ^1.1.6 - version: 1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': specifier: ^1.2.2 - version: 1.2.2(@types/react@19.1.3)(react@19.1.0) + version: 1.2.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-switch': specifier: ^1.2.4 - version: 1.2.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-tabs': specifier: ^1.1.11 - version: 1.1.11(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.11(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-toast': specifier: ^1.2.13 - version: 1.2.13(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.13(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-tooltip': specifier: ^1.2.6 - version: 1.2.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@sentry/nextjs': specifier: 9.17.0 version: 9.17.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.98.0(esbuild@0.25.2)) @@ -116,9 +116,12 @@ importers: geist: specifier: ^1.4.2 version: 1.4.2(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + jotai: + specifier: ^2.12.4 + version: 2.12.4(@types/react@19.1.4)(react@19.1.0) lucide-react: - specifier: ^0.509.0 - version: 0.509.0(react@19.1.0) + specifier: ^0.510.0 + version: 0.510.0(react@19.1.0) next: specifier: 15.3.2 version: 15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -151,7 +154,7 @@ importers: version: 2.0.15(react@19.1.0) react-select: specifier: ^5.10.1 - version: 5.10.1(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 5.10.1(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-use: specifier: 17.6.0 version: 17.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -162,8 +165,8 @@ importers: specifier: ^18.1.0 version: 18.1.0(@types/node@22.15.17) tailwind-merge: - specifier: ^3.2.0 - version: 3.2.0 + specifier: ^3.3.0 + version: 3.3.0 tailwindcss-animate: specifier: ^1.0.7 version: 1.0.7(tailwindcss@4.1.6) @@ -182,7 +185,7 @@ importers: version: 3.3.1 '@storybook/addon-essentials': specifier: 8.6.12 - version: 8.6.12(@types/react@19.1.3)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + version: 8.6.12(@types/react@19.1.4)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) '@storybook/addon-onboarding': specifier: 8.6.12 version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) @@ -209,7 +212,7 @@ importers: version: 6.6.3 '@testing-library/react': specifier: ^16.3.0 - version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@types/eslint': specifier: ^9.6.1 version: 9.6.1 @@ -220,11 +223,11 @@ importers: specifier: ^22.15.17 version: 22.15.17 '@types/react': - specifier: ^19.1.3 - version: 19.1.3 + specifier: ^19.1.4 + version: 19.1.4 '@types/react-dom': - specifier: ^19.1.3 - version: 19.1.3(@types/react@19.1.3) + specifier: ^19.1.4 + version: 19.1.4(@types/react@19.1.4) cross-env: specifier: ^7.0.3 version: 7.0.3 @@ -252,6 +255,9 @@ importers: jest-environment-jsdom: specifier: ^29.7.0 version: 29.7.0(bufferutil@4.0.9) + jotai-devtools: + specifier: ^0.12.0 + version: 0.12.0(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(redux@5.0.1)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) knip: specifier: ^5.55.1 version: 5.55.1(@types/node@22.15.17)(typescript@5.8.3) @@ -1408,6 +1414,12 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' + '@floating-ui/react@0.26.28': + resolution: {integrity: sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + '@floating-ui/utils@0.2.9': resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} @@ -1753,6 +1765,26 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@mantine/code-highlight@7.17.7': + resolution: {integrity: sha512-Gm8uj+eJepY3g6bjqPJ584h68fWtUa00L6P7tTtb61USwmD7RVA1UluE0n3EJxZv0eS/lShsc0DxCTd9B8YMFA==} + peerDependencies: + '@mantine/core': 7.17.7 + '@mantine/hooks': 7.17.7 + react: ^18.x || ^19.x + react-dom: ^18.x || ^19.x + + '@mantine/core@7.17.7': + resolution: {integrity: sha512-JMyV4/jPATXxmE31g8TJxsrH7XvdrA5pwT4gg25zBl1KE5vLBpBLLjsHG728+bDKuf+bDV+8lw+1kqgW7FcplQ==} + peerDependencies: + '@mantine/hooks': 7.17.7 + react: ^18.x || ^19.x + react-dom: ^18.x || ^19.x + + '@mantine/hooks@7.17.7': + resolution: {integrity: sha512-5MIvN/YAcewc97nG3jVrlnFqAExnJRBNWmV6UgGHWbiZiPSCh5o2RJE/5ZVGSfkexDpav9gkm2jkWkIbemVqVA==} + peerDependencies: + react: ^18.x || ^19.x + '@mdx-js/react@3.1.0': resolution: {integrity: sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==} peerDependencies: @@ -2546,6 +2578,11 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + '@redux-devtools/extension@3.3.0': + resolution: {integrity: sha512-X34S/rC8S/M1BIrkYD1mJ5f8vlH0BDqxXrs96cvxSBo4FhMdbhU+GUGsmNYov1xjSyLMHgo8NYrUG8bNX7525g==} + peerDependencies: + redux: ^3.1.0 || ^4.0.0 || ^5.0.0 + '@rollup/plugin-commonjs@28.0.1': resolution: {integrity: sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==} engines: {node: '>=16.0.0 || 14 >= 14.17'} @@ -3183,6 +3220,9 @@ packages: '@types/babel__traverse@7.20.7': resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} + '@types/base16@1.0.5': + resolution: {integrity: sha512-OzOWrTluG9cwqidEzC/Q6FAmIPcnZfm8BFRlIx0+UIUqnuAmi5OS88O0RpT3Yz6qdmqObvUhasrbNsCofE4W9A==} + '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -3228,6 +3268,9 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/lodash@4.17.16': + resolution: {integrity: sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==} + '@types/mdx@2.0.13': resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} @@ -3249,8 +3292,8 @@ packages: '@types/pg@8.6.1': resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} - '@types/react-dom@19.1.3': - resolution: {integrity: sha512-rJXC08OG0h3W6wDMFxQrZF00Kq6qQvw0djHRdzl3U5DnIERz0MRce3WVc7IS6JYBwtaP/DwYtRRjVlvivNveKg==} + '@types/react-dom@19.1.4': + resolution: {integrity: sha512-WxYAszDYgsMV31OVyoG4jbAgJI1Gw0Xq9V19zwhy6+hUUJlJIdZ3r/cbdmTqFv++SktQkZ/X+46yGFxp5XJBEg==} peerDependencies: '@types/react': ^19.0.0 @@ -3259,8 +3302,8 @@ packages: peerDependencies: '@types/react': '*' - '@types/react@19.1.3': - resolution: {integrity: sha512-dLWQ+Z0CkIvK1J8+wrDPwGxEYFA4RAyHoZPxHVGspYmFVnwGSNT24cGIhFJrtfRnWVuW8X7NO52gCXmhkVUWGQ==} + '@types/react@19.1.4': + resolution: {integrity: sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==} '@types/resolve@1.20.6': resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==} @@ -3726,6 +3769,9 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base16@1.0.0: + resolution: {integrity: sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -3939,16 +3985,25 @@ packages: collect-v8-coverage@1.0.2: resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} color-string@1.9.1: resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + color@4.2.3: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} @@ -4226,6 +4281,9 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + diff-match-patch@1.0.5: + resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5018,6 +5076,10 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + hmac-drbg@1.0.1: resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} @@ -5098,6 +5160,9 @@ packages: engines: {node: '>=16.x'} hasBin: true + immutable@4.3.7: + resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -5329,6 +5394,9 @@ packages: engines: {node: '>=10'} hasBin: true + javascript-stringify@2.1.0: + resolution: {integrity: sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==} + jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5479,6 +5547,24 @@ packages: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true + jotai-devtools@0.12.0: + resolution: {integrity: sha512-tPsQHlx8/HKA9zVjzMGWXApOfcQAD/CDzYbBDu0Wsjfj8OCb9QfM9IQQ1rfpXcc+bH90r77aXEL5vo89B+5HOA==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=17.0.0' + + jotai@2.12.4: + resolution: {integrity: sha512-eFXLJol4oOLM8BS1+QV+XwaYQITG8n1tatBCFl4F5HE3zR5j2WIK8QpMt7VJIYmlogNUZfvB7wjwLoVk+umB9Q==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=17.0.0' + react: '>=17.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + js-cookie@2.2.1: resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} @@ -5544,6 +5630,11 @@ packages: engines: {node: '>=6'} hasBin: true + jsondiffpatch@0.5.0: + resolution: {integrity: sha512-Quz3MvAwHxVYNXsOByL7xI5EB2WYOeFswqaHIA3qOK3isRWTxiplBEocmmru6XmxDB2L7jDNYtYA4FyimoAFEw==} + engines: {node: '>=8.17.0'} + hasBin: true + jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -5672,6 +5763,9 @@ packages: resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + lodash.curry@4.1.1: + resolution: {integrity: sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==} + lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} @@ -5700,8 +5794,8 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lucide-react@0.509.0: - resolution: {integrity: sha512-xCJHn6Uh5qF6PGml25vveCTrHJZcqS1G1MVzWZK54ZQsOiCVJk4fwY3oyo5EXS2S+aqvTpWYIfJN+PesJ0quxg==} + lucide-react@0.510.0: + resolution: {integrity: sha512-p8SQRAMVh7NhsAIETokSqDrc5CHnDLbV29mMnzaXx+Vc/hnqQzwI2r0FMWCcoTXnbw2KEjy48xwpGdEL+ck06Q==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -6431,6 +6525,9 @@ packages: resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} engines: {node: '>= 0.8'} + react-base16-styling@0.9.1: + resolution: {integrity: sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==} + react-confetti@6.4.0: resolution: {integrity: sha512-5MdGUcqxrTU26I2EU7ltkWPwxvucQTuqMm8dUz72z2YMqTD6s9vMcDUysk7n9jnC+lXuCPeJJ7Knf98VEYE9Rg==} engines: {node: '>=16'} @@ -6451,6 +6548,11 @@ packages: peerDependencies: react: ^19.1.0 + react-error-boundary@5.0.0: + resolution: {integrity: sha512-tnjAxG+IkpLephNcePNA7v6F/QpWLH8He65+DmedchDwg162JZqx4NmbXj0mlAYVVEd81OW7aFhmbsScYfiAFQ==} + peerDependencies: + react: '>=16.13.1' + react-hook-form@7.56.3: resolution: {integrity: sha512-IK18V6GVbab4TAo1/cz3kqajxbDPGofdF0w7VHdCo0Nt8PrPlOZcuuDq9YYIV1BtjcX78x0XsldbQRQnQXWXmw==} engines: {node: '>=18.0.0'} @@ -6466,11 +6568,23 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-json-tree@0.18.0: + resolution: {integrity: sha512-Qe6HKSXrr++n9Y31nkRJ3XvQMATISpqigH1vEKhLwB56+nk5thTP0ITThpjxY6ZG/ubpVq/aEHIcyLP/OPHxeA==} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-loading-skeleton@3.5.0: resolution: {integrity: sha512-gxxSyLbrEAdXTKgfbpBEFZCO/P153DnqSCQau2+o6lNy1jgMRr2MmRmOzMmyrwSaSYLRB8g7b0waYPmUjz7IhQ==} peerDependencies: react: '>=16.8.0' + react-number-format@5.4.4: + resolution: {integrity: sha512-wOmoNZoOpvMminhifQYiYSTCLUDOiUbBunrMrMjA+dV52sY+vck1S4UhR6PkgnoCquvvMSeJjErXZ4qSaWCliA==} + peerDependencies: + react: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-qr-code@2.0.15: resolution: {integrity: sha512-MkZcjEXqVKqXEIMVE0mbcGgDpkfSdd8zhuzXEl9QzYeNcw8Hq2oVIzDLWuZN2PQBwM5PWjc2S31K8Q1UbcFMfw==} peerDependencies: @@ -6500,6 +6614,12 @@ packages: '@types/react': optional: true + react-resizable-panels@2.1.7: + resolution: {integrity: sha512-JtT6gI+nURzhMYQYsx8DKkx6bSoOGFp7A3CwMrOb8y5jFHFyqwo9m68UhmXRw57fRVJksFn1TSlm3ywEQ9vMgA==} + peerDependencies: + react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-select@5.10.1: resolution: {integrity: sha512-roPEZUL4aRZDx6DcsD+ZNreVl+fM8VsKn0Wtex1v4IazH60ILp5xhdlp464IsEAlJdXeD+BhDAFsBVMfvLQueA==} peerDependencies: @@ -6516,6 +6636,12 @@ packages: '@types/react': optional: true + react-textarea-autosize@8.5.9: + resolution: {integrity: sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-transition-group@4.4.5: resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: @@ -6561,6 +6687,9 @@ packages: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -7062,8 +7191,11 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - tailwind-merge@3.2.0: - resolution: {integrity: sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==} + tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + + tailwind-merge@3.3.0: + resolution: {integrity: sha512-fyW/pEfcQSiigd5SNn0nApUOxx0zB/dm6UDU/rEwc2c3sX2smWUNbapHv+QRqLGVp9GWX3THIa7MUGPo+YkDzQ==} tailwindcss-animate@1.0.7: resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} @@ -7366,6 +7498,15 @@ packages: '@types/react': optional: true + use-composed-ref@1.4.0: + resolution: {integrity: sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + use-isomorphic-layout-effect@1.2.0: resolution: {integrity: sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==} peerDependencies: @@ -7375,6 +7516,15 @@ packages: '@types/react': optional: true + use-latest@1.3.0: + resolution: {integrity: sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + use-sidecar@1.1.3: resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} engines: {node: '>=10'} @@ -8555,7 +8705,7 @@ snapshots: '@emotion/memoize@0.9.0': {} - '@emotion/react@11.14.0(@types/react@19.1.3)(react@19.1.0)': + '@emotion/react@11.14.0(@types/react@19.1.4)(react@19.1.0)': dependencies: '@babel/runtime': 7.26.10 '@emotion/babel-plugin': 11.13.5 @@ -8567,7 +8717,7 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 transitivePeerDependencies: - supports-color @@ -8815,6 +8965,14 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) + '@floating-ui/react@0.26.28(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@floating-ui/react-dom': 2.1.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@floating-ui/utils': 0.2.9 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + tabbable: 6.2.0 + '@floating-ui/utils@0.2.9': {} '@hookform/resolvers@5.0.1(react-hook-form@7.56.3(react@19.1.0))': @@ -9191,10 +9349,37 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@mdx-js/react@3.1.0(@types/react@19.1.3)(react@19.1.0)': + '@mantine/code-highlight@7.17.7(@mantine/core@7.17.7(@mantine/hooks@7.17.7(react@19.1.0))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@7.17.7(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@mantine/core': 7.17.7(@mantine/hooks@7.17.7(react@19.1.0))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mantine/hooks': 7.17.7(react@19.1.0) + clsx: 2.1.1 + highlight.js: 11.11.1 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + '@mantine/core@7.17.7(@mantine/hooks@7.17.7(react@19.1.0))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@floating-ui/react': 0.26.28(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mantine/hooks': 7.17.7(react@19.1.0) + clsx: 2.1.1 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-number-format: 5.4.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-remove-scroll: 2.6.3(@types/react@19.1.4)(react@19.1.0) + react-textarea-autosize: 8.5.9(@types/react@19.1.4)(react@19.1.0) + type-fest: 4.40.0 + transitivePeerDependencies: + - '@types/react' + + '@mantine/hooks@7.17.7(react@19.1.0)': + dependencies: + react: 19.1.0 + + '@mdx-js/react@3.1.0(@types/react@19.1.4)(react@19.1.0)': dependencies: '@types/mdx': 2.0.13 - '@types/react': 19.1.3 + '@types/react': 19.1.4 react: 19.1.0 '@modelcontextprotocol/sdk@1.11.0': @@ -9555,473 +9740,479 @@ snapshots: '@radix-ui/primitive@1.1.2': {} - '@radix-ui/react-arrow@1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-arrow@1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-avatar@1.1.9(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-avatar@1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-checkbox@1.3.1(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-checkbox@1.3.1(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-collapsible@1.1.10(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-collapsible@1.1.10(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-collection@1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-collection@1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.2(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.3)(react@19.1.0)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@radix-ui/react-context@1.1.2(@types/react@19.1.3)(react@19.1.0)': + '@radix-ui/react-context@1.1.2(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@radix-ui/react-dialog@1.1.13(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-dialog@1.1.13(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) aria-hidden: 1.2.4 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - react-remove-scroll: 2.6.3(@types/react@19.1.3)(react@19.1.0) + react-remove-scroll: 2.6.3(@types/react@19.1.4)(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-direction@1.1.1(@types/react@19.1.3)(react@19.1.0)': + '@radix-ui/react-direction@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@radix-ui/react-dismissable-layer@1.1.9(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-dismissable-layer@1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-dropdown-menu@2.1.14(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-dropdown-menu@2.1.14(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-menu': 2.1.14(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-menu': 2.1.14(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-focus-guards@1.1.2(@types/react@19.1.3)(react@19.1.0)': + '@radix-ui/react-focus-guards@1.1.2(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@radix-ui/react-focus-scope@1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-focus-scope@1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-id@1.1.1(@types/react@19.1.3)(react@19.1.0)': + '@radix-ui/react-id@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@radix-ui/react-label@2.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-label@2.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-menu@2.1.14(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-menu@2.1.14(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-popper': 1.2.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.9(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-collection': 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-popper': 1.2.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) aria-hidden: 1.2.4 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - react-remove-scroll: 2.6.3(@types/react@19.1.3)(react@19.1.0) + react-remove-scroll: 2.6.3(@types/react@19.1.4)(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-popover@1.1.13(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-popover@1.1.13(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-popper': 1.2.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-popper': 1.2.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) aria-hidden: 1.2.4 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - react-remove-scroll: 2.6.3(@types/react@19.1.3)(react@19.1.0) + react-remove-scroll: 2.6.3(@types/react@19.1.4)(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-popper@1.2.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-popper@1.2.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@floating-ui/react-dom': 2.1.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-arrow': 1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-arrow': 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.4)(react@19.1.0) '@radix-ui/rect': 1.1.1 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-portal@1.1.8(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-portal@1.1.8(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-presence@1.1.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-presence@1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-primitive@2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-primitive@2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-slot': 1.2.2(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-progress@1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-progress@1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-radio-group@1.3.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-radio-group@1.3.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.9(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-roving-focus@1.1.9(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-roving-focus@1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-collection': 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-separator@1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-separator@1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-slot@1.2.2(@types/react@19.1.3)(react@19.1.0)': + '@radix-ui/react-slot@1.2.2(@types/react@19.1.4)(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@radix-ui/react-switch@1.2.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-switch@1.2.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-tabs@1.1.11(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-tabs@1.1.11(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.9(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-toast@1.2.13(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-toast@1.2.13(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-visually-hidden': 1.2.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-tooltip@1.2.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-tooltip@1.2.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-popper': 1.2.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-visually-hidden': 1.2.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-popper': 1.2.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.3)(react@19.1.0)': + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.3)(react@19.1.0)': + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.4)(react@19.1.0)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.3)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.3)(react@19.1.0)': + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.4)(react@19.1.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.3)(react@19.1.0)': + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.1.3)(react@19.1.0)': + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 use-sync-external-store: 1.5.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.3)(react@19.1.0)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@radix-ui/react-use-previous@1.1.1(@types/react@19.1.3)(react@19.1.0)': + '@radix-ui/react-use-previous@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.1.3)(react@19.1.0)': + '@radix-ui/react-use-rect@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: '@radix-ui/rect': 1.1.1 react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@radix-ui/react-use-size@1.1.1(@types/react@19.1.3)(react@19.1.0)': + '@radix-ui/react-use-size@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.3)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@radix-ui/react-visually-hidden@1.2.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-visually-hidden@1.2.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) '@radix-ui/rect@1.1.1': {} + '@redux-devtools/extension@3.3.0(redux@5.0.1)': + dependencies: + '@babel/runtime': 7.26.10 + immutable: 4.3.7 + redux: 5.0.1 + '@rollup/plugin-commonjs@28.0.1(rollup@4.35.0)': dependencies: '@rollup/pluginutils': 5.1.4(rollup@4.35.0) @@ -10320,9 +10511,9 @@ snapshots: storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) ts-dedent: 2.2.0 - '@storybook/addon-docs@8.6.12(@types/react@19.1.3)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/addon-docs@8.6.12(@types/react@19.1.4)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: - '@mdx-js/react': 3.1.0(@types/react@19.1.3)(react@19.1.0) + '@mdx-js/react': 3.1.0(@types/react@19.1.4)(react@19.1.0) '@storybook/blocks': 8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) '@storybook/csf-plugin': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) '@storybook/react-dom-shim': 8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) @@ -10333,12 +10524,12 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@storybook/addon-essentials@8.6.12(@types/react@19.1.3)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/addon-essentials@8.6.12(@types/react@19.1.4)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: '@storybook/addon-actions': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) '@storybook/addon-backgrounds': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) '@storybook/addon-controls': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - '@storybook/addon-docs': 8.6.12(@types/react@19.1.3)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/addon-docs': 8.6.12(@types/react@19.1.4)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) '@storybook/addon-highlight': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) '@storybook/addon-measure': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) '@storybook/addon-outline': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) @@ -10759,15 +10950,15 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/react@16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@testing-library/react@16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.26.10 '@testing-library/dom': 10.4.0 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 - '@types/react-dom': 19.1.3(@types/react@19.1.3) + '@types/react': 19.1.4 + '@types/react-dom': 19.1.4(@types/react@19.1.4) '@testing-library/user-event@14.5.2(@testing-library/dom@10.4.0)': dependencies: @@ -10806,6 +10997,8 @@ snapshots: dependencies: '@babel/types': 7.27.0 + '@types/base16@1.0.5': {} + '@types/connect@3.4.38': dependencies: '@types/node': 22.15.17 @@ -10857,6 +11050,8 @@ snapshots: '@types/json5@0.0.29': {} + '@types/lodash@4.17.16': {} + '@types/mdx@2.0.13': {} '@types/mysql@2.15.26': @@ -10885,15 +11080,15 @@ snapshots: pg-protocol: 1.7.1 pg-types: 2.2.0 - '@types/react-dom@19.1.3(@types/react@19.1.3)': + '@types/react-dom@19.1.4(@types/react@19.1.4)': dependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@types/react-transition-group@4.4.12(@types/react@19.1.3)': + '@types/react-transition-group@4.4.12(@types/react@19.1.4)': dependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - '@types/react@19.1.3': + '@types/react@19.1.4': dependencies: csstype: 3.1.3 @@ -11484,6 +11679,8 @@ snapshots: balanced-match@1.0.2: {} + base16@1.0.0: {} + base64-js@1.5.1: {} better-opn@3.0.2: @@ -11715,17 +11912,27 @@ snapshots: collect-v8-coverage@1.0.2: {} + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + color-convert@2.0.1: dependencies: color-name: 1.1.4 + color-name@1.1.3: {} + color-name@1.1.4: {} color-string@1.9.1: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 - optional: true + + color@3.2.1: + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 color@4.2.3: dependencies: @@ -12002,6 +12209,8 @@ snapshots: detect-node-es@1.1.0: {} + diff-match-patch@1.0.5: {} + diff-sequences@29.6.3: {} diff@4.0.2: {} @@ -12950,6 +13159,8 @@ snapshots: he@1.2.0: {} + highlight.js@11.11.1: {} + hmac-drbg@1.0.1: dependencies: hash.js: 1.1.7 @@ -13040,6 +13251,8 @@ snapshots: dependencies: queue: 6.0.2 + immutable@4.3.7: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -13093,8 +13306,7 @@ snapshots: is-arrayish@0.2.1: {} - is-arrayish@0.3.2: - optional: true + is-arrayish@0.3.2: {} is-async-function@2.1.1: dependencies: @@ -13294,6 +13506,8 @@ snapshots: filelist: 1.0.4 minimatch: 3.1.2 + javascript-stringify@2.1.0: {} + jest-changed-files@29.7.0: dependencies: execa: 5.1.1 @@ -13628,6 +13842,33 @@ snapshots: jiti@2.4.2: {} + jotai-devtools@0.12.0(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(redux@5.0.1)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)): + dependencies: + '@mantine/code-highlight': 7.17.7(@mantine/core@7.17.7(@mantine/hooks@7.17.7(react@19.1.0))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@7.17.7(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mantine/core': 7.17.7(@mantine/hooks@7.17.7(react@19.1.0))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mantine/hooks': 7.17.7(react@19.1.0) + '@redux-devtools/extension': 3.3.0(redux@5.0.1) + '@storybook/test': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + clsx: 2.1.1 + javascript-stringify: 2.1.0 + jotai: 2.12.4(@types/react@19.1.4)(react@19.1.0) + jsondiffpatch: 0.5.0 + react: 19.1.0 + react-base16-styling: 0.9.1 + react-error-boundary: 5.0.0(react@19.1.0) + react-json-tree: 0.18.0(@types/react@19.1.4)(react@19.1.0) + react-resizable-panels: 2.1.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + transitivePeerDependencies: + - '@types/react' + - react-dom + - redux + - storybook + + jotai@2.12.4(@types/react@19.1.4)(react@19.1.0): + optionalDependencies: + '@types/react': 19.1.4 + react: 19.1.0 + js-cookie@2.2.1: {} js-cookie@3.0.5: {} @@ -13698,6 +13939,11 @@ snapshots: json5@2.2.3: {} + jsondiffpatch@0.5.0: + dependencies: + chalk: 3.0.0 + diff-match-patch: 1.0.5 + jsonfile@6.1.0: dependencies: universalify: 2.0.1 @@ -13817,6 +14063,8 @@ snapshots: dependencies: p-locate: 6.0.0 + lodash.curry@4.1.1: {} + lodash.debounce@4.0.8: {} lodash.memoize@4.1.2: {} @@ -13841,7 +14089,7 @@ snapshots: dependencies: yallist: 3.1.1 - lucide-react@0.509.0(react@19.1.0): + lucide-react@0.510.0(react@19.1.0): dependencies: react: 19.1.0 @@ -14501,6 +14749,16 @@ snapshots: iconv-lite: 0.6.3 unpipe: 1.0.0 + react-base16-styling@0.9.1: + dependencies: + '@babel/runtime': 7.26.10 + '@types/base16': 1.0.5 + '@types/lodash': 4.17.16 + base16: 1.0.0 + color: 3.2.1 + csstype: 3.1.3 + lodash.curry: 4.1.1 + react-confetti@6.4.0(react@19.1.0): dependencies: react: 19.1.0 @@ -14530,6 +14788,11 @@ snapshots: react: 19.1.0 scheduler: 0.26.0 + react-error-boundary@5.0.0(react@19.1.0): + dependencies: + '@babel/runtime': 7.26.10 + react: 19.1.0 + react-hook-form@7.56.3(react@19.1.0): dependencies: react: 19.1.0 @@ -14540,10 +14803,23 @@ snapshots: react-is@18.3.1: {} + react-json-tree@0.18.0(@types/react@19.1.4)(react@19.1.0): + dependencies: + '@babel/runtime': 7.26.10 + '@types/lodash': 4.17.16 + '@types/react': 19.1.4 + react: 19.1.0 + react-base16-styling: 0.9.1 + react-loading-skeleton@3.5.0(react@19.1.0): dependencies: react: 19.1.0 + react-number-format@5.4.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-qr-code@2.0.15(react@19.1.0): dependencies: prop-types: 15.8.1 @@ -14552,49 +14828,63 @@ snapshots: react-refresh@0.14.2: {} - react-remove-scroll-bar@2.3.8(@types/react@19.1.3)(react@19.1.0): + react-remove-scroll-bar@2.3.8(@types/react@19.1.4)(react@19.1.0): dependencies: react: 19.1.0 - react-style-singleton: 2.2.3(@types/react@19.1.3)(react@19.1.0) + react-style-singleton: 2.2.3(@types/react@19.1.4)(react@19.1.0) tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - react-remove-scroll@2.6.3(@types/react@19.1.3)(react@19.1.0): + react-remove-scroll@2.6.3(@types/react@19.1.4)(react@19.1.0): dependencies: react: 19.1.0 - react-remove-scroll-bar: 2.3.8(@types/react@19.1.3)(react@19.1.0) - react-style-singleton: 2.2.3(@types/react@19.1.3)(react@19.1.0) + react-remove-scroll-bar: 2.3.8(@types/react@19.1.4)(react@19.1.0) + react-style-singleton: 2.2.3(@types/react@19.1.4)(react@19.1.0) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.1.3)(react@19.1.0) - use-sidecar: 1.1.3(@types/react@19.1.3)(react@19.1.0) + use-callback-ref: 1.3.3(@types/react@19.1.4)(react@19.1.0) + use-sidecar: 1.1.3(@types/react@19.1.4)(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 + + react-resizable-panels@2.1.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) - react-select@5.10.1(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + react-select@5.10.1(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@babel/runtime': 7.26.10 '@emotion/cache': 11.14.0 - '@emotion/react': 11.14.0(@types/react@19.1.3)(react@19.1.0) + '@emotion/react': 11.14.0(@types/react@19.1.4)(react@19.1.0) '@floating-ui/dom': 1.6.13 - '@types/react-transition-group': 4.4.12(@types/react@19.1.3) + '@types/react-transition-group': 4.4.12(@types/react@19.1.4) memoize-one: 6.0.0 prop-types: 15.8.1 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) react-transition-group: 4.4.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - use-isomorphic-layout-effect: 1.2.0(@types/react@19.1.3)(react@19.1.0) + use-isomorphic-layout-effect: 1.2.0(@types/react@19.1.4)(react@19.1.0) transitivePeerDependencies: - '@types/react' - supports-color - react-style-singleton@2.2.3(@types/react@19.1.3)(react@19.1.0): + react-style-singleton@2.2.3(@types/react@19.1.4)(react@19.1.0): dependencies: get-nonce: 1.0.1 react: 19.1.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 + + react-textarea-autosize@8.5.9(@types/react@19.1.4)(react@19.1.0): + dependencies: + '@babel/runtime': 7.26.10 + react: 19.1.0 + use-composed-ref: 1.4.0(@types/react@19.1.4)(react@19.1.0) + use-latest: 1.3.0(@types/react@19.1.4)(react@19.1.0) + transitivePeerDependencies: + - '@types/react' react-transition-group@4.4.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: @@ -14672,6 +14962,8 @@ snapshots: indent-string: 4.0.0 strip-indent: 3.0.0 + redux@5.0.1: {} + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -15058,7 +15350,6 @@ snapshots: simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 - optional: true sisteransi@1.0.5: {} @@ -15289,7 +15580,9 @@ snapshots: symbol-tree@3.2.4: {} - tailwind-merge@3.2.0: {} + tabbable@6.2.0: {} + + tailwind-merge@3.3.0: {} tailwindcss-animate@1.0.7(tailwindcss@4.1.6): dependencies: @@ -15575,26 +15868,39 @@ snapshots: punycode: 1.4.1 qs: 6.14.0 - use-callback-ref@1.3.3(@types/react@19.1.3)(react@19.1.0): + use-callback-ref@1.3.3(@types/react@19.1.4)(react@19.1.0): dependencies: react: 19.1.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 + + use-composed-ref@1.4.0(@types/react@19.1.4)(react@19.1.0): + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.4 + + use-isomorphic-layout-effect@1.2.0(@types/react@19.1.4)(react@19.1.0): + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.4 - use-isomorphic-layout-effect@1.2.0(@types/react@19.1.3)(react@19.1.0): + use-latest@1.3.0(@types/react@19.1.4)(react@19.1.0): dependencies: react: 19.1.0 + use-isomorphic-layout-effect: 1.2.0(@types/react@19.1.4)(react@19.1.0) optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 - use-sidecar@1.1.3(@types/react@19.1.3)(react@19.1.0): + use-sidecar@1.1.3(@types/react@19.1.4)(react@19.1.0): dependencies: detect-node-es: 1.1.0 react: 19.1.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.4 use-sync-external-store@1.5.0(react@19.1.0): dependencies: diff --git a/src/app/p/[locationSlug]/_components/PublicFooter.tsx b/src/app/p/[locationSlug]/_components/PublicFooter.tsx new file mode 100644 index 0000000..7a004c6 --- /dev/null +++ b/src/app/p/[locationSlug]/_components/PublicFooter.tsx @@ -0,0 +1,3 @@ +export async function PublicFooter(props: {}) { + return <>footer; +} diff --git a/src/app/p/[locationSlug]/layout.tsx b/src/app/p/[locationSlug]/layout.tsx index 16bf829..5de11e4 100644 --- a/src/app/p/[locationSlug]/layout.tsx +++ b/src/app/p/[locationSlug]/layout.tsx @@ -1,6 +1,7 @@ import { cookies } from 'next/headers'; import Image from 'next/image'; import { PostHog } from 'posthog-node'; +import { PublicFooter } from '~/app/p/[locationSlug]/_components/PublicFooter'; import { AnalyticsEventSender } from '~/components/AnalyticsEventSender'; import type { AnalyticsEventId } from '~/domain/analytics'; import { CookieKey } from '~/domain/cookies'; @@ -76,6 +77,8 @@ export default async function Layout({ params, children }: { params: Params; chi
{children}
+ + { - pr.removeAllListeners(); + pr.off('paymentmethod'); }; }, [stripe, elements, amount, clientSecret, merchantName, onError, onSuccess]); From 2047041cc629e47f7d854f87602e682b8c50b802 Mon Sep 17 00:00:00 2001 From: dopoto Date: Tue, 13 May 2025 10:33:15 +0300 Subject: [PATCH 05/47] wip --- .../actions/createMenuItemPaymentIntent.ts | 9 ++-- .../_components/PublicFooter.tsx | 53 ++++++++++++++++++- src/components/public/PublicMenuItem.tsx | 15 +++--- src/lib/payment-utils.ts | 17 +++--- 4 files changed, 68 insertions(+), 26 deletions(-) diff --git a/src/app/actions/createMenuItemPaymentIntent.ts b/src/app/actions/createMenuItemPaymentIntent.ts index 5c59096..d36902b 100644 --- a/src/app/actions/createMenuItemPaymentIntent.ts +++ b/src/app/actions/createMenuItemPaymentIntent.ts @@ -1,18 +1,15 @@ 'use server'; -import { type MenuItem } from '~/domain/menu-items'; import { type PaymentIntentResponse } from '~/domain/payments'; import { createPaymentIntent, verifyConnectAccount } from '~/lib/payment-utils'; export async function createMenuItemPaymentIntent( - item: MenuItem, + amount: number, merchantStripeAccountId: string, ): Promise { try { - if (!item || !item.price || !item.name) { - throw new Error('Invalid menu item data'); - } if (!merchantStripeAccountId || !merchantStripeAccountId.startsWith('acct_')) { + //TODO throw new Error('Invalid merchant Stripe account'); } @@ -22,7 +19,7 @@ export async function createMenuItemPaymentIntent( throw new Error('Merchant account is not fully set up for payments yet'); } - const paymentIntent = await createPaymentIntent(item, merchantStripeAccountId); + const paymentIntent = await createPaymentIntent(amount, merchantStripeAccountId); if (!paymentIntent.client_secret) { throw new Error('No client secret received from Stripe'); diff --git a/src/app/p/[locationSlug]/_components/PublicFooter.tsx b/src/app/p/[locationSlug]/_components/PublicFooter.tsx index 7a004c6..03b268b 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooter.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooter.tsx @@ -1,3 +1,52 @@ -export async function PublicFooter(props: {}) { - return <>footer; +'use client'; + +import { LoaderIcon } from 'lucide-react'; +import { useState } from 'react'; +import { createMenuItemPaymentIntent } from '~/app/actions/createMenuItemPaymentIntent'; +import { Button } from '~/components/ui/button'; +import { CURRENCIES, type CurrencyId } from '~/domain/currencies'; +import type { MenuItem } from '~/domain/menu-items'; +import type { PaymentIntentResponse } from '~/domain/payments'; + +export function PublicFooter(props: { currencyId: CurrencyId; item: MenuItem; merchantStripeAccountId: string }) { + const [amount, setAmount] = useState(100); + + const currency = CURRENCIES[props.currencyId]; + const [paymentIntent, setPaymentIntent] = useState(null); + const [error, setError] = useState(null); + const [isLoading, setIsLoading] = useState(false); + + return ( + <> +
amount: {amount}
+ {amount && amount > 0 && ( + + )} + + ); } diff --git a/src/components/public/PublicMenuItem.tsx b/src/components/public/PublicMenuItem.tsx index 9aa3d7a..46a612c 100644 --- a/src/components/public/PublicMenuItem.tsx +++ b/src/components/public/PublicMenuItem.tsx @@ -1,14 +1,11 @@ 'use client'; -import { LoaderIcon, SoupIcon, WineIcon } from 'lucide-react'; +import { PlusIcon, SoupIcon, WineIcon } from 'lucide-react'; import { useState } from 'react'; -import { createMenuItemPaymentIntent } from '~/app/actions/createMenuItemPaymentIntent'; import { Badge } from '~/components/ui/badge'; -import { Button } from '~/components/ui/button'; import { CURRENCIES, type CurrencyId } from '~/domain/currencies'; import { type MenuItem } from '~/domain/menu-items'; import { type PaymentIntentResponse } from '~/domain/payments'; -import { PaymentButton } from './PaymentButton'; export function PublicMenuItem(props: { item: MenuItem; currencyId: CurrencyId; merchantStripeAccountId: string }) { const { name, description, price, isNew, type } = props.item; @@ -16,6 +13,9 @@ export function PublicMenuItem(props: { item: MenuItem; currencyId: CurrencyId; const [paymentIntent, setPaymentIntent] = useState(null); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); + + const addToOrder = () => {}; + return (
@@ -32,8 +32,8 @@ export function PublicMenuItem(props: { item: MenuItem; currencyId: CurrencyId;
{description}
{' '}
- {price} {currency.symbol}{' '} - {paymentIntent ? ( + {price} {currency.symbol} + {/* {paymentIntent ? ( - )} + )} */}
{error &&
{error}
} +
); diff --git a/src/lib/payment-utils.ts b/src/lib/payment-utils.ts index 13412b5..0f79427 100644 --- a/src/lib/payment-utils.ts +++ b/src/lib/payment-utils.ts @@ -1,5 +1,4 @@ import Stripe from 'stripe'; -import { type MenuItem } from '~/domain/menu-items'; import { env } from '~/env'; const stripe = new Stripe(env.STRIPE_SECRET_KEY); @@ -14,24 +13,20 @@ export function formatStripeAmount(price: number): number { return Math.round(price * 100); } -export async function createPaymentIntent(item: MenuItem, merchantStripeAccountId: string) { - // Convert price string to number and format for Stripe - const priceNum = parseFloat(item.price); - if (isNaN(priceNum)) { - throw new Error('Invalid price format'); - } +export async function createPaymentIntent(priceNum: number, merchantStripeAccountId: string) { const amountInCents = formatStripeAmount(priceNum); const applicationFeeAmount = calculateAppFee(amountInCents); return stripe.paymentIntents.create( { amount: amountInCents, - currency: 'usd', + currency: 'usd', //TODO payment_method_types: ['card'], // Digital wallets will be handled by the Payment Request Button application_fee_amount: applicationFeeAmount, metadata: { - menuItemId: item.id, - menuItemName: item.name, - type: item.type, + // TODO Orderid + // menuItemId: item.id, + // menuItemName: item.name, + // type: item.type, merchantId: merchantStripeAccountId, }, }, From 0a27804b06a9a7e702e17f1c0c69d403b6273782 Mon Sep 17 00:00:00 2001 From: dopoto Date: Tue, 13 May 2025 13:51:45 +0300 Subject: [PATCH 06/47] wip footer checkout --- src/app/actions/createCartPaymentIntent.ts | 52 +++++ src/app/api/webhooks/stripe/route.ts | 4 +- .../_components/PublicFooter.tsx | 194 ++++++++++++++---- .../[locationSlug]/_components/PublicMenu.tsx | 9 +- src/app/p/[locationSlug]/layout.tsx | 3 +- src/components/public/PublicMenuItem.tsx | 75 +++---- src/domain/cart.ts | 9 + 7 files changed, 247 insertions(+), 99 deletions(-) create mode 100644 src/app/actions/createCartPaymentIntent.ts create mode 100644 src/domain/cart.ts diff --git a/src/app/actions/createCartPaymentIntent.ts b/src/app/actions/createCartPaymentIntent.ts new file mode 100644 index 0000000..270f3e7 --- /dev/null +++ b/src/app/actions/createCartPaymentIntent.ts @@ -0,0 +1,52 @@ +'use server'; + +import { CartItem } from '~/domain/cart'; +import { type PaymentIntentResponse } from '~/domain/payments'; +import { createPaymentIntent, verifyConnectAccount } from '~/lib/payment-utils'; +import { getMenuItemsByLocation } from '~/server/queries/menu-items'; + +export async function createCartPaymentIntent( + cartItems: CartItem[], + locationId: number, +): Promise { + try { + // TODO validate location + + //TODO actual id: + const merchantStripeAccountId = 'acct_1RNsp3CEmU5ANDgp'; + + // Verify that the Connect account is properly set up + const isValidAccount = await verifyConnectAccount(merchantStripeAccountId); + if (!isValidAccount) { + throw new Error('Merchant account is not fully set up for payments yet'); + } + + const menuItems = await getMenuItemsByLocation(locationId); + + const totalAmount = cartItems.reduce((sum, item) => { + const matchedItem = menuItems.find((mi) => mi.id === item.menuItem.id); + const matchedItemPrice = matchedItem ? matchedItem.price : '0'; //TODO handle? + return sum + parseFloat(matchedItemPrice) * item.quantity; + }, 0); + + const paymentIntent = await createPaymentIntent(totalAmount, merchantStripeAccountId); + + if (!paymentIntent.client_secret) { + throw new Error('No client secret received from Stripe'); + } + + return { + clientSecret: paymentIntent.client_secret, + paymentIntentId: paymentIntent.id, + }; + } catch (error) { + console.error('Failed to create cart payment intent:', error); + if (error instanceof Error) { + if (error.message.includes('Stripe')) { + throw new Error('Payment service temporarily unavailable. Please try again.'); + } + throw new Error(error.message); + } + throw new Error('Failed to initiate payment. Please try again.'); + } +} diff --git a/src/app/api/webhooks/stripe/route.ts b/src/app/api/webhooks/stripe/route.ts index 56c6e01..05edfca 100644 --- a/src/app/api/webhooks/stripe/route.ts +++ b/src/app/api/webhooks/stripe/route.ts @@ -7,11 +7,11 @@ import { AppError } from '~/lib/error-utils.server'; import { processFailedPayment, processSuccessfulPayment } from '~/lib/payment-utils'; const stripe = new Stripe(env.STRIPE_SECRET_KEY); -const webhookSecret = env.STRIPE_WEBHOOK_SECRET; +const webhookSecret = 'TODO'; //env.STRIPE_WEBHOOK_SECRET; export async function POST(request: Request) { const body = await request.text(); - const headersList = headers(); + const headersList = await headers(); const signature = headersList.get('stripe-signature'); if (!signature) { diff --git a/src/app/p/[locationSlug]/_components/PublicFooter.tsx b/src/app/p/[locationSlug]/_components/PublicFooter.tsx index 03b268b..759a2f0 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooter.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooter.tsx @@ -1,52 +1,172 @@ 'use client'; -import { LoaderIcon } from 'lucide-react'; +import { Elements } from '@stripe/react-stripe-js'; +import { loadStripe } from '@stripe/stripe-js'; +import { useAtom } from 'jotai'; +import { LoaderIcon, ShoppingCart } from 'lucide-react'; import { useState } from 'react'; -import { createMenuItemPaymentIntent } from '~/app/actions/createMenuItemPaymentIntent'; +import { createCartPaymentIntent } from '~/app/actions/createCartPaymentIntent'; +import { PaymentButton } from '~/components/public/PaymentButton'; import { Button } from '~/components/ui/button'; +import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '~/components/ui/sheet'; +import { cartAtom } from '~/domain/cart'; import { CURRENCIES, type CurrencyId } from '~/domain/currencies'; -import type { MenuItem } from '~/domain/menu-items'; -import type { PaymentIntentResponse } from '~/domain/payments'; +import { LocationId } from '~/domain/locations'; +import { env } from '~/env'; +import { useToast } from '~/hooks/use-toast'; -export function PublicFooter(props: { currencyId: CurrencyId; item: MenuItem; merchantStripeAccountId: string }) { - const [amount, setAmount] = useState(100); +const stripePromise = loadStripe(env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY); +export function PublicFooter(props: { currencyId: CurrencyId; locationId: LocationId }) { const currency = CURRENCIES[props.currencyId]; - const [paymentIntent, setPaymentIntent] = useState(null); - const [error, setError] = useState(null); + const [cart, setCart] = useAtom(cartAtom); + const [clientSecret, setClientSecret] = useState(null); const [isLoading, setIsLoading] = useState(false); + const [isCheckingOut, setIsCheckingOut] = useState(false); + const { toast } = useToast(); + + const totalAmount = cart.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0') * item.quantity, 0); + + const updateItemQuantity = (itemId: number, delta: number) => { + setCart( + cart + .map((item) => { + if (item.menuItem.id !== itemId) return item; + const newQuantity = item.quantity + delta; + return newQuantity > 0 ? { ...item, quantity: newQuantity } : item; + }) + .filter((item) => item.quantity > 0), + ); + }; + + const handleCheckout = async () => { + setIsLoading(true); + try { + const paymentIntent = await createCartPaymentIntent(cart, props.locationId); + setClientSecret(paymentIntent.clientSecret); + setIsCheckingOut(true); + } catch (err) { + toast({ + title: 'Error', + description: err instanceof Error ? err.message : 'Failed to initiate checkout', + }); + } finally { + setIsLoading(false); + } + }; + + const handlePaymentSuccess = () => { + toast({ + title: 'Success', + description: 'Payment completed successfully!', + }); + setCart([]); // Clear cart + setIsCheckingOut(false); + }; + + const handlePaymentError = (error: Error) => { + toast({ + title: 'Payment Failed', + description: error.message, + }); + setIsCheckingOut(false); + }; + + console.log(JSON.stringify(cart, null, 2)); return ( - <> -
amount: {amount}
- {amount && amount > 0 && ( - - )} - + + + + Your Order + + {cart.length === 0 ? ( +
+

Your cart is empty

+
+ ) : ( + <> +
+ {/* {cart.map((item) => ( +
+
+

{item.menuItem.name}

+

+ {item.menuItem.price} {currency.symbol} × {item.quantity} +

+
+
+ + {item.quantity} + +
+
+ ))} */} +
+ +
+
+ Total: + + {totalAmount.toFixed(2)} {currency.symbol} + +
+ {isCheckingOut && clientSecret ? ( +
+ + + +
+ ) : ( + + )} +
+ + )} +
+ ); } diff --git a/src/app/p/[locationSlug]/_components/PublicMenu.tsx b/src/app/p/[locationSlug]/_components/PublicMenu.tsx index ef3cb67..38508c0 100644 --- a/src/app/p/[locationSlug]/_components/PublicMenu.tsx +++ b/src/app/p/[locationSlug]/_components/PublicMenu.tsx @@ -7,14 +7,7 @@ export async function PublicMenu(props: { name: string; currencyId: CurrencyId; <>

{props.name}

{props.items?.map((item) => { - return ( - - ); + return ; })} ); diff --git a/src/app/p/[locationSlug]/layout.tsx b/src/app/p/[locationSlug]/layout.tsx index 5de11e4..55f200e 100644 --- a/src/app/p/[locationSlug]/layout.tsx +++ b/src/app/p/[locationSlug]/layout.tsx @@ -77,7 +77,8 @@ export default async function Layout({ params, children }: { params: Params; chi
{children}
- + {/* TODO */} + ; currencyId: CurrencyId }) { const { name, description, price, isNew, type } = props.item; const currency = CURRENCIES[props.currencyId]; - const [paymentIntent, setPaymentIntent] = useState(null); - const [error, setError] = useState(null); - const [isLoading, setIsLoading] = useState(false); + const [cart, setCart] = useAtom(cartAtom); - const addToOrder = () => {}; + const addToOrder = () => { + const existingItem = cart.find((item) => item.menuItem.id === props.item.id); + if (existingItem) { + setCart( + cart.map((item) => + item.menuItem.id === props.item.id ? { ...item, quantity: item.quantity + 1 } : item, + ), + ); + } else { + setCart([...cart, { menuItem: props.item, quantity: 1 }]); + } + toast({ + title: 'Added to cart', + description: `${name} has been added to your order.`, + }); + }; return (
@@ -33,51 +47,10 @@ export function PublicMenuItem(props: { item: MenuItem; currencyId: CurrencyId;
{description}
{' '}
{price} {currency.symbol} - {/* {paymentIntent ? ( - { - setPaymentIntent(null); - setError(null); - }} - onError={(err) => setError(err.message)} - /> - ) : ( - - )} */}
- {error &&
{error}
} - +
+
+
); diff --git a/src/domain/cart.ts b/src/domain/cart.ts new file mode 100644 index 0000000..0094382 --- /dev/null +++ b/src/domain/cart.ts @@ -0,0 +1,9 @@ +import { atom } from 'jotai'; +import { type MenuItem } from './menu-items'; + +export interface CartItem { + menuItem: Partial; + quantity: number; +} + +export const cartAtom = atom([]); From 1b03d4a18aa255a221726bbe121a72588fb29236 Mon Sep 17 00:00:00 2001 From: dopoto Date: Tue, 13 May 2025 14:06:21 +0300 Subject: [PATCH 07/47] wip --- .../_components/PublicFooter.tsx | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/app/p/[locationSlug]/_components/PublicFooter.tsx b/src/app/p/[locationSlug]/_components/PublicFooter.tsx index 759a2f0..d88f180 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooter.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooter.tsx @@ -3,7 +3,7 @@ import { Elements } from '@stripe/react-stripe-js'; import { loadStripe } from '@stripe/stripe-js'; import { useAtom } from 'jotai'; -import { LoaderIcon, ShoppingCart } from 'lucide-react'; +import { LoaderIcon, MinusIcon, PlusIcon, ShoppingCart } from 'lucide-react'; import { useState } from 'react'; import { createCartPaymentIntent } from '~/app/actions/createCartPaymentIntent'; import { PaymentButton } from '~/components/public/PaymentButton'; @@ -77,7 +77,7 @@ export function PublicFooter(props: { currencyId: CurrencyId; locationId: Locati return ( - {item.quantity} - ))} */} + ))}
From 7a7fc19658c80b894c46cdd0f6ad7664bc01c602 Mon Sep 17 00:00:00 2001 From: dopoto Date: Tue, 13 May 2025 14:20:39 +0300 Subject: [PATCH 08/47] fix Pay button --- src/app/actions/createCartPaymentIntent.ts | 8 +++-- .../_components/PublicFooter.tsx | 33 +++++++------------ src/components/public/PaymentButton.tsx | 9 +++-- src/domain/payments.ts | 1 + 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/app/actions/createCartPaymentIntent.ts b/src/app/actions/createCartPaymentIntent.ts index 270f3e7..35ad3f7 100644 --- a/src/app/actions/createCartPaymentIntent.ts +++ b/src/app/actions/createCartPaymentIntent.ts @@ -10,9 +10,11 @@ export async function createCartPaymentIntent( locationId: number, ): Promise { try { - // TODO validate location + if (!locationId) { + throw new Error('Location ID is required'); + } - //TODO actual id: + // TODO const merchantStripeAccountId = 'acct_1RNsp3CEmU5ANDgp'; // Verify that the Connect account is properly set up @@ -34,10 +36,10 @@ export async function createCartPaymentIntent( if (!paymentIntent.client_secret) { throw new Error('No client secret received from Stripe'); } - return { clientSecret: paymentIntent.client_secret, paymentIntentId: paymentIntent.id, + merchantStripeAccountId: merchantStripeAccountId, }; } catch (error) { console.error('Failed to create cart payment intent:', error); diff --git a/src/app/p/[locationSlug]/_components/PublicFooter.tsx b/src/app/p/[locationSlug]/_components/PublicFooter.tsx index d88f180..c4dcddd 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooter.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooter.tsx @@ -1,7 +1,5 @@ 'use client'; -import { Elements } from '@stripe/react-stripe-js'; -import { loadStripe } from '@stripe/stripe-js'; import { useAtom } from 'jotai'; import { LoaderIcon, MinusIcon, PlusIcon, ShoppingCart } from 'lucide-react'; import { useState } from 'react'; @@ -12,17 +10,15 @@ import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '~/co import { cartAtom } from '~/domain/cart'; import { CURRENCIES, type CurrencyId } from '~/domain/currencies'; import { LocationId } from '~/domain/locations'; -import { env } from '~/env'; import { useToast } from '~/hooks/use-toast'; -const stripePromise = loadStripe(env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY); - export function PublicFooter(props: { currencyId: CurrencyId; locationId: LocationId }) { const currency = CURRENCIES[props.currencyId]; const [cart, setCart] = useAtom(cartAtom); const [clientSecret, setClientSecret] = useState(null); const [isLoading, setIsLoading] = useState(false); const [isCheckingOut, setIsCheckingOut] = useState(false); + const [merchantStripeAccountId, setMerchantStripeAccountId] = useState(null); const { toast } = useToast(); const totalAmount = cart.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0') * item.quantity, 0); @@ -38,12 +34,12 @@ export function PublicFooter(props: { currencyId: CurrencyId; locationId: Locati .filter((item) => item.quantity > 0), ); }; - const handleCheckout = async () => { setIsLoading(true); try { const paymentIntent = await createCartPaymentIntent(cart, props.locationId); setClientSecret(paymentIntent.clientSecret); + setMerchantStripeAccountId(paymentIntent.merchantStripeAccountId); setIsCheckingOut(true); } catch (err) { toast({ @@ -139,23 +135,16 @@ export function PublicFooter(props: { currencyId: CurrencyId; locationId: Locati {totalAmount.toFixed(2)} {currency.symbol}
- {isCheckingOut && clientSecret ? ( + {isCheckingOut && clientSecret && merchantStripeAccountId ? (
- - - +
) : ( +
+ + + Your Order + + {cart.length === 0 ? ( +
+

Your cart is empty

+
+ ) : ( + <> +
+ {cart.map((item) => ( +
+
+

{item.menuItem.name}

+

+ {item.menuItem.price} {currency.symbol} × {item.quantity} +

+
+
+ + {item.quantity} + +
+
+ ))} +
+ +
+
+ Total: + + {totalAmount.toFixed(2)} {currency.symbol} + +
+ {isCheckingOut && clientSecret && merchantStripeAccountId ? ( +
+ +
+ ) : ( + + )} +
+ + )} +
+
+ ); +} diff --git a/src/app/p/[locationSlug]/_components/PublicFooter.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx similarity index 98% rename from src/app/p/[locationSlug]/_components/PublicFooter.tsx rename to src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx index c4dcddd..162f65a 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooter.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx @@ -12,7 +12,7 @@ import { CURRENCIES, type CurrencyId } from '~/domain/currencies'; import { LocationId } from '~/domain/locations'; import { useToast } from '~/hooks/use-toast'; -export function PublicFooter(props: { currencyId: CurrencyId; locationId: LocationId }) { +export function PublicFooterPrepaidMode(props: { currencyId: CurrencyId; locationId: LocationId }) { const currency = CURRENCIES[props.currencyId]; const [cart, setCart] = useAtom(cartAtom); const [clientSecret, setClientSecret] = useState(null); diff --git a/src/app/p/[locationSlug]/layout.tsx b/src/app/p/[locationSlug]/layout.tsx index 55f200e..52ea291 100644 --- a/src/app/p/[locationSlug]/layout.tsx +++ b/src/app/p/[locationSlug]/layout.tsx @@ -1,7 +1,8 @@ import { cookies } from 'next/headers'; import Image from 'next/image'; import { PostHog } from 'posthog-node'; -import { PublicFooter } from '~/app/p/[locationSlug]/_components/PublicFooter'; +import { PublicFooterOrderOnlyMode } from '~/app/p/[locationSlug]/_components/PublicFooterOrderOnlyMode'; +import { PublicFooterPrepaidMode } from '~/app/p/[locationSlug]/_components/PublicFooterPrepaidMode'; import { AnalyticsEventSender } from '~/components/AnalyticsEventSender'; import type { AnalyticsEventId } from '~/domain/analytics'; import { CookieKey } from '~/domain/cookies'; @@ -77,8 +78,10 @@ export default async function Layout({ params, children }: { params: Params; chi
{children}
- {/* TODO */} - + {location.menuMode === 'prepaid' && } + {location.menuMode === 'orderonly' && ( + + )} ; currencyId: CurrencyId; menuMode: MenuModeId }) { +export function PublicMenuItem(props: { item: MenuItem; currencyId: CurrencyId; menuMode: MenuModeId }) { const { name, description, price, isNew, type } = props.item; const currency = CURRENCIES[props.currencyId]; const [cart, setCart] = useAtom(cartAtom); @@ -23,7 +23,8 @@ export function PublicMenuItem(props: { item: Partial; currencyId: Cur ), ); } else { - setCart([...cart, { menuItem: props.item, quantity: 1 }]); + const initialStatus: CartItem['status'] = 'draft'; + setCart([...cart, { menuItem: props.item, quantity: 1, status: initialStatus }]); } toast({ title: 'Added to cart', diff --git a/src/domain/cart.ts b/src/domain/cart.ts index 0094382..a13090e 100644 --- a/src/domain/cart.ts +++ b/src/domain/cart.ts @@ -1,9 +1,13 @@ import { atom } from 'jotai'; import { type MenuItem } from './menu-items'; +type OrderOnlyCartItemStatus = 'draft' | 'ordered' | 'delivered' | 'paid'; +type PrepaidCartItemStatus = 'draft' | 'paid'; + export interface CartItem { - menuItem: Partial; + menuItem: Pick; quantity: number; + status: OrderOnlyCartItemStatus | PrepaidCartItemStatus; } export const cartAtom = atom([]); From e4012aaa275497d5fe1e70de04e07326fb8d3e05 Mon Sep 17 00:00:00 2001 From: dopoto Date: Wed, 14 May 2025 14:27:28 +0300 Subject: [PATCH 13/47] your order --- package.json | 1 + pnpm-lock.yaml | 19 ++ src/app/actions/createCartPaymentIntent.ts | 2 +- .../_components/PublicFooterDrawer.tsx | 47 +++++ .../_components/PublicFooterOrderOnlyMode.tsx | 188 +++++------------- .../_components/PublicFooterPrepaidMode.tsx | 41 +--- src/components/public/PublicMenuItem.tsx | 13 +- src/components/ui/drawer.tsx | 132 ++++++++++++ src/domain/cart.ts | 4 +- 9 files changed, 255 insertions(+), 192 deletions(-) create mode 100644 src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx create mode 100644 src/components/ui/drawer.tsx diff --git a/package.json b/package.json index f82acb4..c29df52 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "tailwind-merge": "^3.3.0", "tailwindcss-animate": "^1.0.7", "truncate-middle": "^2.0.1", + "vaul": "^1.1.2", "zod": "3.24.4" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e8c85ff..3a53344 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -173,6 +173,9 @@ importers: truncate-middle: specifier: ^2.0.1 version: 2.0.1 + vaul: + specifier: ^1.1.2 + version: 1.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) zod: specifier: 3.24.4 version: 3.24.4 @@ -5634,6 +5637,7 @@ packages: resolution: {integrity: sha512-Quz3MvAwHxVYNXsOByL7xI5EB2WYOeFswqaHIA3qOK3isRWTxiplBEocmmru6XmxDB2L7jDNYtYA4FyimoAFEw==} engines: {node: '>=8.17.0'} hasBin: true + bundledDependencies: [] jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -7564,6 +7568,12 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + vaul@1.1.2: + resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + vm-browserify@1.1.2: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} @@ -15930,6 +15940,15 @@ snapshots: vary@1.1.2: {} + vaul@1.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + '@radix-ui/react-dialog': 1.1.13(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + vm-browserify@1.1.2: {} w3c-xmlserializer@4.0.0: diff --git a/src/app/actions/createCartPaymentIntent.ts b/src/app/actions/createCartPaymentIntent.ts index 35ad3f7..3827c80 100644 --- a/src/app/actions/createCartPaymentIntent.ts +++ b/src/app/actions/createCartPaymentIntent.ts @@ -28,7 +28,7 @@ export async function createCartPaymentIntent( const totalAmount = cartItems.reduce((sum, item) => { const matchedItem = menuItems.find((mi) => mi.id === item.menuItem.id); const matchedItemPrice = matchedItem ? matchedItem.price : '0'; //TODO handle? - return sum + parseFloat(matchedItemPrice) * item.quantity; + return sum + parseFloat(matchedItemPrice); }, 0); const paymentIntent = await createPaymentIntent(totalAmount, merchantStripeAccountId); diff --git a/src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx b/src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx new file mode 100644 index 0000000..82e2b8c --- /dev/null +++ b/src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx @@ -0,0 +1,47 @@ +'use client'; + +import { useState } from 'react'; +import { Drawer, DrawerClose, DrawerContent, DrawerFooter, DrawerTrigger } from '~/components/ui/drawer'; +import { cn } from '~/lib/utils'; + +interface FooterDrawerProps { + collapsedContent?: React.ReactNode; + children?: React.ReactNode; + className?: string; + triggerClassName?: string; + contentClassName?: string; +} + +export function PublicFooterDrawer({ + collapsedContent, + children, + className, + triggerClassName, + contentClassName, +}: FooterDrawerProps) { + const [open, setOpen] = useState(false); + + return ( + + +
+ {collapsedContent} +
+
+ +
+
{children}
+ + Close + +
+
+
+ ); +} diff --git a/src/app/p/[locationSlug]/_components/PublicFooterOrderOnlyMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterOrderOnlyMode.tsx index b594bde..8a15637 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterOrderOnlyMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterOrderOnlyMode.tsx @@ -1,17 +1,22 @@ 'use client'; import { useAtom } from 'jotai'; -import { LoaderIcon, MinusIcon, PlusIcon, ShoppingCart } from 'lucide-react'; import { useState } from 'react'; -import { createCartPaymentIntent } from '~/app/actions/createCartPaymentIntent'; -import { PaymentButton } from '~/components/public/PaymentButton'; -import { Button } from '~/components/ui/button'; -import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '~/components/ui/sheet'; +import { PublicFooterDrawer } from '~/app/p/[locationSlug]/_components/PublicFooterDrawer'; import { cartAtom } from '~/domain/cart'; import { CURRENCIES, type CurrencyId } from '~/domain/currencies'; import { LocationId } from '~/domain/locations'; import { useToast } from '~/hooks/use-toast'; +function OrderSummaryItem(props: { quantity: number; description: string }) { + return ( +
+
{props.quantity}
+
{props.description}
+
+ ); +} + export function PublicFooterOrderOnlyMode(props: { currencyId: CurrencyId; locationId: LocationId }) { const currency = CURRENCIES[props.currencyId]; const [cart, setCart] = useAtom(cartAtom); @@ -21,147 +26,46 @@ export function PublicFooterOrderOnlyMode(props: { currencyId: CurrencyId; locat const [merchantStripeAccountId, setMerchantStripeAccountId] = useState(null); const { toast } = useToast(); - const totalAmount = cart.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0') * item.quantity, 0); - - const updateItemQuantity = (itemId: number, delta: number) => { - setCart( - cart - .map((item) => { - if (item.menuItem.id !== itemId) return item; - const newQuantity = item.quantity + delta; - return newQuantity > 0 ? { ...item, quantity: newQuantity } : item; - }) - .filter((item) => item.quantity > 0), - ); - }; - const handleCheckout = async () => { - setIsLoading(true); - try { - const paymentIntent = await createCartPaymentIntent(cart, props.locationId); - setClientSecret(paymentIntent.clientSecret); - setMerchantStripeAccountId(paymentIntent.merchantStripeAccountId); - setIsCheckingOut(true); - } catch (err) { - toast({ - title: 'Error', - description: err instanceof Error ? err.message : 'Failed to initiate checkout', - }); - } finally { - setIsLoading(false); - } - }; + const totalAmount = cart.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0'), 0); - const handlePaymentSuccess = () => { - toast({ - title: 'Success', - description: 'Payment completed successfully!', - }); - setCart([]); // Clear cart - setIsCheckingOut(false); - }; + console.log(JSON.stringify(cart, null, 2)); - const handlePaymentError = (error: Error) => { - toast({ - title: 'Payment Failed', - description: error.message, - }); - setIsCheckingOut(false); - }; + const draftItems = cart.filter((item) => item.status === 'draft').length; + const inPreparationItems = cart.filter((item) => item.status === 'ordered').length; + const deliveredItems = cart.filter((item) => item.status === 'delivered').length; - console.log(JSON.stringify(cart, null, 2)); + const collapsedContent = ( +
+
Your order
+
+
+ +
+
+ +
+
+ +
+
+
+ ); return ( - - - - - - - Your Order - - {cart.length === 0 ? ( -
-

Your cart is empty

-
- ) : ( - <> -
- {cart.map((item) => ( -
-
-

{item.menuItem.name}

-

- {item.menuItem.price} {currency.symbol} × {item.quantity} -

-
-
- - {item.quantity} - -
-
- ))} -
- -
-
- Total: - - {totalAmount.toFixed(2)} {currency.symbol} - -
- {isCheckingOut && clientSecret && merchantStripeAccountId ? ( -
- -
- ) : ( - - )} -
- - )} -
-
+ +
+

This is the content of the drawer. You can put anything here.

+
+

Example Content

+

+ This could be settings, additional information, or any other content. +

+
+
+

More Content

+

Add as much content as you need here.

+
+
+
); } diff --git a/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx index 162f65a..1aa372a 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx @@ -1,7 +1,7 @@ 'use client'; import { useAtom } from 'jotai'; -import { LoaderIcon, MinusIcon, PlusIcon, ShoppingCart } from 'lucide-react'; +import { LoaderIcon, ShoppingCart } from 'lucide-react'; import { useState } from 'react'; import { createCartPaymentIntent } from '~/app/actions/createCartPaymentIntent'; import { PaymentButton } from '~/components/public/PaymentButton'; @@ -21,19 +21,8 @@ export function PublicFooterPrepaidMode(props: { currencyId: CurrencyId; locatio const [merchantStripeAccountId, setMerchantStripeAccountId] = useState(null); const { toast } = useToast(); - const totalAmount = cart.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0') * item.quantity, 0); + const totalAmount = cart.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0'), 0); - const updateItemQuantity = (itemId: number, delta: number) => { - setCart( - cart - .map((item) => { - if (item.menuItem.id !== itemId) return item; - const newQuantity = item.quantity + delta; - return newQuantity > 0 ? { ...item, quantity: newQuantity } : item; - }) - .filter((item) => item.quantity > 0), - ); - }; const handleCheckout = async () => { setIsLoading(true); try { @@ -77,7 +66,7 @@ export function PublicFooterPrepaidMode(props: { currencyId: CurrencyId; locatio {cart.length > 0 && ( - {cart.reduce((sum, item) => sum + item.quantity, 0)} + {cart.reduce((sum, item) => sum + Number(item.menuItem.price), 0)} )} @@ -98,31 +87,11 @@ export function PublicFooterPrepaidMode(props: { currencyId: CurrencyId; locatio

{item.menuItem.name}

- {item.menuItem.price} {currency.symbol} × {item.quantity} + {item.menuItem.price} {currency.symbol}

- - {item.quantity} - + 1
))} diff --git a/src/components/public/PublicMenuItem.tsx b/src/components/public/PublicMenuItem.tsx index 806aca4..b8d21e3 100644 --- a/src/components/public/PublicMenuItem.tsx +++ b/src/components/public/PublicMenuItem.tsx @@ -15,17 +15,8 @@ export function PublicMenuItem(props: { item: MenuItem; currencyId: CurrencyId; const [cart, setCart] = useAtom(cartAtom); const addToOrder = () => { - const existingItem = cart.find((item) => item.menuItem.id === props.item.id); - if (existingItem) { - setCart( - cart.map((item) => - item.menuItem.id === props.item.id ? { ...item, quantity: item.quantity + 1 } : item, - ), - ); - } else { - const initialStatus: CartItem['status'] = 'draft'; - setCart([...cart, { menuItem: props.item, quantity: 1, status: initialStatus }]); - } + const initialStatus: CartItem['status'] = 'draft'; + setCart([...cart, { menuItem: props.item, status: initialStatus }]); toast({ title: 'Added to cart', description: `${name} has been added to your order.`, diff --git a/src/components/ui/drawer.tsx b/src/components/ui/drawer.tsx new file mode 100644 index 0000000..9d091e1 --- /dev/null +++ b/src/components/ui/drawer.tsx @@ -0,0 +1,132 @@ +"use client" + +import * as React from "react" +import { Drawer as DrawerPrimitive } from "vaul" + +import { cn } from "~/lib/utils" + +function Drawer({ + ...props +}: React.ComponentProps) { + return +} + +function DrawerTrigger({ + ...props +}: React.ComponentProps) { + return +} + +function DrawerPortal({ + ...props +}: React.ComponentProps) { + return +} + +function DrawerClose({ + ...props +}: React.ComponentProps) { + return +} + +function DrawerOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DrawerContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + +
+ {children} + + + ) +} + +function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DrawerTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DrawerDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + Drawer, + DrawerPortal, + DrawerOverlay, + DrawerTrigger, + DrawerClose, + DrawerContent, + DrawerHeader, + DrawerFooter, + DrawerTitle, + DrawerDescription, +} diff --git a/src/domain/cart.ts b/src/domain/cart.ts index a13090e..5464cf2 100644 --- a/src/domain/cart.ts +++ b/src/domain/cart.ts @@ -5,8 +5,8 @@ type OrderOnlyCartItemStatus = 'draft' | 'ordered' | 'delivered' | 'paid'; type PrepaidCartItemStatus = 'draft' | 'paid'; export interface CartItem { - menuItem: Pick; - quantity: number; + menuItem: Pick; + status: OrderOnlyCartItemStatus | PrepaidCartItemStatus; } From 984a3135c749d170932dc68570ec92c284daf9b7 Mon Sep 17 00:00:00 2001 From: dopoto Date: Thu, 15 May 2025 06:39:31 +0300 Subject: [PATCH 14/47] fix ts --- .../actions/createMenuItemPaymentIntent.ts | 1 + .../menu-items/_components/MenuItemCard.tsx | 9 ++++- .../menus/_components/MenuItemSelector.tsx | 12 ++----- .../menus/_components/SortableMenuItem.tsx | 9 ++--- src/domain/cart.ts | 7 ++-- src/domain/order-items.ts | 14 ++++++++ src/server/db/schema.ts | 34 +++++++++++++++++++ src/server/queries/locations.ts | 18 ++++++++-- 8 files changed, 79 insertions(+), 25 deletions(-) create mode 100644 src/domain/order-items.ts diff --git a/src/app/actions/createMenuItemPaymentIntent.ts b/src/app/actions/createMenuItemPaymentIntent.ts index d36902b..137b822 100644 --- a/src/app/actions/createMenuItemPaymentIntent.ts +++ b/src/app/actions/createMenuItemPaymentIntent.ts @@ -28,6 +28,7 @@ export async function createMenuItemPaymentIntent( return { clientSecret: paymentIntent.client_secret, paymentIntentId: paymentIntent.id, + merchantStripeAccountId, }; } catch (error) { console.error('Failed to create payment intent:', error); diff --git a/src/app/u/[locationId]/menu-items/_components/MenuItemCard.tsx b/src/app/u/[locationId]/menu-items/_components/MenuItemCard.tsx index bc28b5e..935f968 100644 --- a/src/app/u/[locationId]/menu-items/_components/MenuItemCard.tsx +++ b/src/app/u/[locationId]/menu-items/_components/MenuItemCard.tsx @@ -49,15 +49,22 @@ export default function MenuItemCard(props: { locationId: LocationId; currencyId return ( <>
+ {/* TODO menumode */}
diff --git a/src/app/u/[locationId]/menus/_components/MenuItemSelector.tsx b/src/app/u/[locationId]/menus/_components/MenuItemSelector.tsx index de3cfa4..e2483df 100644 --- a/src/app/u/[locationId]/menus/_components/MenuItemSelector.tsx +++ b/src/app/u/[locationId]/menus/_components/MenuItemSelector.tsx @@ -34,16 +34,8 @@ export function MenuItemSelector({ allMenuItems, addedItems, currencyId, onSelec className="cursor-pointer rounded-lg border p-4 hover:bg-gray-50" onClick={() => onSelect(item)} > - + {/* TODO menumode */} +
))}
diff --git a/src/app/u/[locationId]/menus/_components/SortableMenuItem.tsx b/src/app/u/[locationId]/menus/_components/SortableMenuItem.tsx index 10d4960..a086caa 100644 --- a/src/app/u/[locationId]/menus/_components/SortableMenuItem.tsx +++ b/src/app/u/[locationId]/menus/_components/SortableMenuItem.tsx @@ -41,14 +41,11 @@ export function SortableMenuItem({ item, currencyId, onDelete }: SortableMenuIte
+ {/* TODO menumode */}
+ + Your order +
{children}
From 1f8d503cf60436fc4a2bee75b23d760a58a57bae Mon Sep 17 00:00:00 2001 From: dopoto Date: Thu, 15 May 2025 06:58:21 +0300 Subject: [PATCH 16/47] wip --- ...nlyMode.tsx => PublicFooterPostpaidMode.tsx} | 17 ++++++++++++++--- src/app/p/[locationSlug]/layout.tsx | 6 +++--- src/components/public/PublicMenuItem.tsx | 2 +- src/domain/menu-modes.ts | 4 ++-- src/domain/order-items.ts | 9 ++------- 5 files changed, 22 insertions(+), 16 deletions(-) rename src/app/p/[locationSlug]/_components/{PublicFooterOrderOnlyMode.tsx => PublicFooterPostpaidMode.tsx} (84%) diff --git a/src/app/p/[locationSlug]/_components/PublicFooterOrderOnlyMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx similarity index 84% rename from src/app/p/[locationSlug]/_components/PublicFooterOrderOnlyMode.tsx rename to src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx index 8a15637..8d78522 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterOrderOnlyMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx @@ -3,21 +3,23 @@ import { useAtom } from 'jotai'; import { useState } from 'react'; import { PublicFooterDrawer } from '~/app/p/[locationSlug]/_components/PublicFooterDrawer'; +import { Button } from '~/components/ui/button'; import { cartAtom } from '~/domain/cart'; import { CURRENCIES, type CurrencyId } from '~/domain/currencies'; import { LocationId } from '~/domain/locations'; import { useToast } from '~/hooks/use-toast'; -function OrderSummaryItem(props: { quantity: number; description: string }) { +function OrderSummaryItem(props: { quantity: number; description: string; children?: React.ReactNode }) { return (
{props.quantity}
{props.description}
+
{props.children}
); } -export function PublicFooterOrderOnlyMode(props: { currencyId: CurrencyId; locationId: LocationId }) { +export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locationId: LocationId }) { const currency = CURRENCIES[props.currencyId]; const [cart, setCart] = useAtom(cartAtom); const [clientSecret, setClientSecret] = useState(null); @@ -30,6 +32,13 @@ export function PublicFooterOrderOnlyMode(props: { currencyId: CurrencyId; locat console.log(JSON.stringify(cart, null, 2)); + const order = (e?: React.MouseEvent) => { + if (e) { + e.stopPropagation(); + } + console.log('order'); + }; + const draftItems = cart.filter((item) => item.status === 'draft').length; const inPreparationItems = cart.filter((item) => item.status === 'ordered').length; const deliveredItems = cart.filter((item) => item.status === 'delivered').length; @@ -39,7 +48,9 @@ export function PublicFooterOrderOnlyMode(props: { currencyId: CurrencyId; locat
Your order
- + + {draftItems > 0 && } +
diff --git a/src/app/p/[locationSlug]/layout.tsx b/src/app/p/[locationSlug]/layout.tsx index 52ea291..d4c2e83 100644 --- a/src/app/p/[locationSlug]/layout.tsx +++ b/src/app/p/[locationSlug]/layout.tsx @@ -1,7 +1,7 @@ import { cookies } from 'next/headers'; import Image from 'next/image'; import { PostHog } from 'posthog-node'; -import { PublicFooterOrderOnlyMode } from '~/app/p/[locationSlug]/_components/PublicFooterOrderOnlyMode'; +import { PublicFooterPostpaidMode } from '~/app/p/[locationSlug]/_components/PublicFooterPostpaidMode'; import { PublicFooterPrepaidMode } from '~/app/p/[locationSlug]/_components/PublicFooterPrepaidMode'; import { AnalyticsEventSender } from '~/components/AnalyticsEventSender'; import type { AnalyticsEventId } from '~/domain/analytics'; @@ -79,8 +79,8 @@ export default async function Layout({ params, children }: { params: Params; chi
{children}
{location.menuMode === 'prepaid' && } - {location.menuMode === 'orderonly' && ( - + {location.menuMode === 'postpaid' && ( + )} diff --git a/src/domain/menu-modes.ts b/src/domain/menu-modes.ts index 105f003..7a04dbc 100644 --- a/src/domain/menu-modes.ts +++ b/src/domain/menu-modes.ts @@ -1,7 +1,7 @@ -export type MenuModeId = 'noninteractive' | 'orderonly' | 'prepaid'; +export type MenuModeId = 'noninteractive' | 'postpaid' | 'prepaid'; export const MENU_MODES: Record = { noninteractive: '', - orderonly: '', + postpaid: '', prepaid: '', }; diff --git a/src/domain/order-items.ts b/src/domain/order-items.ts index 4231dd1..00a714e 100644 --- a/src/domain/order-items.ts +++ b/src/domain/order-items.ts @@ -1,14 +1,9 @@ -// type OrderOnlyOrderItemStatus = 'draft' | 'ordered' | 'delivered' | 'paid'; -// type PrepaidOrderItemStatus = 'draft' | 'paid'; - -// export type OrderItemStatus = OrderOnlyOrderItemStatus | PrepaidOrderItemStatus; - export const ORDER_ONLY_STATUSES = ['draft', 'ordered', 'delivered', 'paid'] as const; export const PREPAID_STATUSES = ['draft', 'paid'] as const; export const ORDER_ITEM_STATUSES = [...new Set([...ORDER_ONLY_STATUSES, ...PREPAID_STATUSES])] as const; -type OrderOnlyOrderItemStatus = (typeof ORDER_ONLY_STATUSES)[number]; +type PostpaidOrderItemStatus = (typeof ORDER_ONLY_STATUSES)[number]; type PrepaidOrderItemStatus = (typeof PREPAID_STATUSES)[number]; -export type OrderItemStatus = OrderOnlyOrderItemStatus | PrepaidOrderItemStatus; +export type OrderItemStatus = PostpaidOrderItemStatus | PrepaidOrderItemStatus; From 32158332407cdbd21968f7f38f36702021e2ee6b Mon Sep 17 00:00:00 2001 From: dopoto Date: Thu, 15 May 2025 07:16:51 +0300 Subject: [PATCH 17/47] wip --- src/app/actions/placeOrderAction.ts | 55 +++++++++++++++++++ .../_components/PublicFooterPostpaidMode.tsx | 42 +++++++++++++- src/lib/order-utils.ts | 9 +++ 3 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 src/app/actions/placeOrderAction.ts create mode 100644 src/lib/order-utils.ts diff --git a/src/app/actions/placeOrderAction.ts b/src/app/actions/placeOrderAction.ts new file mode 100644 index 0000000..1ab64db --- /dev/null +++ b/src/app/actions/placeOrderAction.ts @@ -0,0 +1,55 @@ +'use server'; + +import { CartItem } from '~/domain/cart'; +import { LocationId } from '~/domain/locations'; + +/** + * Generates a unique order number using a timestamp and random characters + */ +function generateUniqueOrderNumber(): string { + const timestamp = new Date().getTime().toString(36).toUpperCase(); + const randomStr = Math.random().toString(36).substring(2, 6).toUpperCase(); + return `ORD-${timestamp}${randomStr}`; +} + +/** + * Places an order by storing it in the database and updating the order items status + */ +export async function placeOrderAction( + cartItems: CartItem[], + locationId: LocationId +): Promise<{ orderNumber: string }> { + try { + // Filter only draft items for the order + const draftItems = cartItems.filter((item) => item.status === 'draft'); + + if (draftItems.length === 0) { + throw new Error('No draft items to order'); + } + + // Generate a unique order number + // In a real implementation, this would be stored in the database + const orderNumber = generateUniqueOrderNumber(); + + console.log(`Creating order ${orderNumber} for location ${locationId} with ${draftItems.length} items`); + + // Here we would typically: + // 1. Create an order record in the database + // 2. Create order item records for each draft item + // 3. Update the status of those items to 'ordered' + + // Since we don't have direct database access in this example, + // we're just simulating a successful order creation + + // In a real implementation, this would be a database transaction + + // Return the order number so it can be displayed to the user + return { orderNumber }; + } catch (error) { + console.error('Failed to place order:', error); + if (error instanceof Error) { + throw new Error(error.message); + } + throw new Error('Failed to place order. Please try again.'); + } +} diff --git a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx index 8d78522..0479f15 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx @@ -2,6 +2,7 @@ import { useAtom } from 'jotai'; import { useState } from 'react'; +import { placeOrderAction } from '~/app/actions/placeOrderAction'; import { PublicFooterDrawer } from '~/app/p/[locationSlug]/_components/PublicFooterDrawer'; import { Button } from '~/components/ui/button'; import { cartAtom } from '~/domain/cart'; @@ -32,11 +33,44 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati console.log(JSON.stringify(cart, null, 2)); - const order = (e?: React.MouseEvent) => { + const order = async (e?: React.MouseEvent) => { if (e) { e.stopPropagation(); + e.preventDefault(); + } + + try { + setIsLoading(true); + + // Call the server action to place the order + const { orderNumber } = await placeOrderAction(cart, props.locationId); + + // Update the cart items status from 'draft' to 'ordered' + setCart(prevCart => { + return prevCart.map(item => { + if (item.status === 'draft') { + return { ...item, status: 'ordered' }; + } + return item; + }); + }); + + // Show success message + toast({ + title: 'Order placed successfully', + description: `Your order number is ${orderNumber}`, + variant: 'default', + }); + } catch (error) { + console.error('Failed to place order:', error); + toast({ + title: 'Failed to place order', + description: error instanceof Error ? error.message : 'Please try again', + variant: 'destructive', + }); + } finally { + setIsLoading(false); } - console.log('order'); }; const draftItems = cart.filter((item) => item.status === 'draft').length; @@ -49,7 +83,9 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati
- {draftItems > 0 && } + {draftItems > 0 && }
diff --git a/src/lib/order-utils.ts b/src/lib/order-utils.ts new file mode 100644 index 0000000..73e8d0d --- /dev/null +++ b/src/lib/order-utils.ts @@ -0,0 +1,9 @@ +/** + * Generates a unique order number using a timestamp and random characters + * In a real implementation, this would be handled by the database + */ +export function generateUniqueOrderNumber(): string { + const timestamp = new Date().getTime().toString(36).toUpperCase(); + const randomStr = Math.random().toString(36).substring(2, 6).toUpperCase(); + return `ORD-${timestamp}${randomStr}`; +} From 179efb289cf6afb94c8b2e96a96189207f80f28d Mon Sep 17 00:00:00 2001 From: dopoto Date: Thu, 15 May 2025 10:01:53 +0300 Subject: [PATCH 18/47] items --- src/app/actions/createCartPaymentIntent.ts | 4 +- src/app/actions/placeOrderAction.ts | 27 ++++---- .../_components/PublicFooterPostpaidMode.tsx | 65 +++++++++---------- .../_components/PublicFooterPrepaidMode.tsx | 20 +++--- src/app/p/[locationSlug]/_state/cart.ts | 16 +++++ src/components/public/PublicMenuItem.tsx | 14 ++-- src/domain/cart.ts | 10 --- src/domain/orders.ts | 8 +++ 8 files changed, 91 insertions(+), 73 deletions(-) create mode 100644 src/app/p/[locationSlug]/_state/cart.ts delete mode 100644 src/domain/cart.ts create mode 100644 src/domain/orders.ts diff --git a/src/app/actions/createCartPaymentIntent.ts b/src/app/actions/createCartPaymentIntent.ts index 3827c80..676b815 100644 --- a/src/app/actions/createCartPaymentIntent.ts +++ b/src/app/actions/createCartPaymentIntent.ts @@ -1,12 +1,12 @@ 'use server'; -import { CartItem } from '~/domain/cart'; +import { PublicOrderItem } from '~/app/p/[locationSlug]/_state/cart'; import { type PaymentIntentResponse } from '~/domain/payments'; import { createPaymentIntent, verifyConnectAccount } from '~/lib/payment-utils'; import { getMenuItemsByLocation } from '~/server/queries/menu-items'; export async function createCartPaymentIntent( - cartItems: CartItem[], + cartItems: PublicOrderItem[], locationId: number, ): Promise { try { diff --git a/src/app/actions/placeOrderAction.ts b/src/app/actions/placeOrderAction.ts index 1ab64db..3d7182a 100644 --- a/src/app/actions/placeOrderAction.ts +++ b/src/app/actions/placeOrderAction.ts @@ -1,7 +1,8 @@ 'use server'; -import { CartItem } from '~/domain/cart'; +import { PublicOrderItem } from '~/app/p/[locationSlug]/_state/cart'; import { LocationId } from '~/domain/locations'; +import { OrderId } from '~/domain/orders'; /** * Generates a unique order number using a timestamp and random characters @@ -16,35 +17,35 @@ function generateUniqueOrderNumber(): string { * Places an order by storing it in the database and updating the order items status */ export async function placeOrderAction( - cartItems: CartItem[], - locationId: LocationId -): Promise<{ orderNumber: string }> { + cartItems: PublicOrderItem[], + locationId: LocationId, +): Promise<{ orderId: OrderId }> { try { // Filter only draft items for the order const draftItems = cartItems.filter((item) => item.status === 'draft'); - + if (draftItems.length === 0) { throw new Error('No draft items to order'); } // Generate a unique order number // In a real implementation, this would be stored in the database - const orderNumber = generateUniqueOrderNumber(); - - console.log(`Creating order ${orderNumber} for location ${locationId} with ${draftItems.length} items`); - + const orderId = generateUniqueOrderNumber(); + + console.log(`Creating order ${orderId} for location ${locationId} with ${draftItems.length} items`); + // Here we would typically: // 1. Create an order record in the database // 2. Create order item records for each draft item // 3. Update the status of those items to 'ordered' - + // Since we don't have direct database access in this example, // we're just simulating a successful order creation - + // In a real implementation, this would be a database transaction - + // Return the order number so it can be displayed to the user - return { orderNumber }; + return { orderId }; } catch (error) { console.error('Failed to place order:', error); if (error instanceof Error) { diff --git a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx index 0479f15..5313bea 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx @@ -4,9 +4,9 @@ import { useAtom } from 'jotai'; import { useState } from 'react'; import { placeOrderAction } from '~/app/actions/placeOrderAction'; import { PublicFooterDrawer } from '~/app/p/[locationSlug]/_components/PublicFooterDrawer'; +import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; import { Button } from '~/components/ui/button'; -import { cartAtom } from '~/domain/cart'; -import { CURRENCIES, type CurrencyId } from '~/domain/currencies'; +import { type CurrencyId } from '~/domain/currencies'; import { LocationId } from '~/domain/locations'; import { useToast } from '~/hooks/use-toast'; @@ -21,44 +21,39 @@ function OrderSummaryItem(props: { quantity: number; description: string; childr } export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locationId: LocationId }) { - const currency = CURRENCIES[props.currencyId]; - const [cart, setCart] = useAtom(cartAtom); - const [clientSecret, setClientSecret] = useState(null); + const [order, setOrder] = useAtom(orderAtom); const [isLoading, setIsLoading] = useState(false); - const [isCheckingOut, setIsCheckingOut] = useState(false); - const [merchantStripeAccountId, setMerchantStripeAccountId] = useState(null); const { toast } = useToast(); - const totalAmount = cart.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0'), 0); + const totalAmount = order.items.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0'), 0); - console.log(JSON.stringify(cart, null, 2)); - - const order = async (e?: React.MouseEvent) => { + const processOrder = async (e?: React.MouseEvent) => { if (e) { e.stopPropagation(); e.preventDefault(); } - + try { setIsLoading(true); - - // Call the server action to place the order - const { orderNumber } = await placeOrderAction(cart, props.locationId); - - // Update the cart items status from 'draft' to 'ordered' - setCart(prevCart => { - return prevCart.map(item => { - if (item.status === 'draft') { - return { ...item, status: 'ordered' }; - } - return item; - }); + + const { orderId } = await placeOrderAction(order.items, props.locationId); + + setOrder((prevOrder) => { + return { + ...prevOrder, + orderId, + items: prevOrder.items.map((item) => { + if (item.status === 'draft') { + return { ...item, status: 'ordered' }; + } + return item; + }), + }; }); - - // Show success message + toast({ title: 'Order placed successfully', - description: `Your order number is ${orderNumber}`, + description: `Your order number is ${orderId}`, variant: 'default', }); } catch (error) { @@ -73,19 +68,21 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati } }; - const draftItems = cart.filter((item) => item.status === 'draft').length; - const inPreparationItems = cart.filter((item) => item.status === 'ordered').length; - const deliveredItems = cart.filter((item) => item.status === 'delivered').length; + const draftItems = order.items.filter((item) => item.status === 'draft').length; + const inPreparationItems = order.items.filter((item) => item.status === 'ordered').length; + const deliveredItems = order.items.filter((item) => item.status === 'delivered').length; const collapsedContent = (
-
Your order
+
Your order {order.orderId}
- {draftItems > 0 && } + {draftItems > 0 && ( + + )}
diff --git a/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx index 1aa372a..24577da 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx @@ -4,29 +4,29 @@ import { useAtom } from 'jotai'; import { LoaderIcon, ShoppingCart } from 'lucide-react'; import { useState } from 'react'; import { createCartPaymentIntent } from '~/app/actions/createCartPaymentIntent'; +import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; import { PaymentButton } from '~/components/public/PaymentButton'; import { Button } from '~/components/ui/button'; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '~/components/ui/sheet'; -import { cartAtom } from '~/domain/cart'; import { CURRENCIES, type CurrencyId } from '~/domain/currencies'; import { LocationId } from '~/domain/locations'; import { useToast } from '~/hooks/use-toast'; export function PublicFooterPrepaidMode(props: { currencyId: CurrencyId; locationId: LocationId }) { const currency = CURRENCIES[props.currencyId]; - const [cart, setCart] = useAtom(cartAtom); + const [order, setOrder] = useAtom(orderAtom); const [clientSecret, setClientSecret] = useState(null); const [isLoading, setIsLoading] = useState(false); const [isCheckingOut, setIsCheckingOut] = useState(false); const [merchantStripeAccountId, setMerchantStripeAccountId] = useState(null); const { toast } = useToast(); - const totalAmount = cart.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0'), 0); + const totalAmount = order.items.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0'), 0); const handleCheckout = async () => { setIsLoading(true); try { - const paymentIntent = await createCartPaymentIntent(cart, props.locationId); + const paymentIntent = await createCartPaymentIntent(order.items, props.locationId); setClientSecret(paymentIntent.clientSecret); setMerchantStripeAccountId(paymentIntent.merchantStripeAccountId); setIsCheckingOut(true); @@ -45,7 +45,7 @@ export function PublicFooterPrepaidMode(props: { currencyId: CurrencyId; locatio title: 'Success', description: 'Payment completed successfully!', }); - setCart([]); // Clear cart + //setOrder( ); setIsCheckingOut(false); }; @@ -57,16 +57,16 @@ export function PublicFooterPrepaidMode(props: { currencyId: CurrencyId; locatio setIsCheckingOut(false); }; - console.log(JSON.stringify(cart, null, 2)); + console.log(JSON.stringify(order, null, 2)); return ( @@ -75,14 +75,14 @@ export function PublicFooterPrepaidMode(props: { currencyId: CurrencyId; locatio Your Order - {cart.length === 0 ? ( + {order.items.length === 0 ? (

Your cart is empty

) : ( <>
- {cart.map((item) => ( + {order.items.map((item) => (

{item.menuItem.name}

diff --git a/src/app/p/[locationSlug]/_state/cart.ts b/src/app/p/[locationSlug]/_state/cart.ts new file mode 100644 index 0000000..6a36319 --- /dev/null +++ b/src/app/p/[locationSlug]/_state/cart.ts @@ -0,0 +1,16 @@ +import { atom } from 'jotai'; +import { OrderItemStatus } from '~/domain/order-items'; +import { OrderId } from '~/domain/orders'; +import { type MenuItem } from '../../../../domain/menu-items'; + +export interface PublicOrder { + orderId: OrderId | null; + items: PublicOrderItem[]; +} + +export interface PublicOrderItem { + menuItem: Pick; + status: OrderItemStatus; +} + +export const orderAtom = atom({ orderId: null, items: [] }); diff --git a/src/components/public/PublicMenuItem.tsx b/src/components/public/PublicMenuItem.tsx index 14cd840..66ae7c1 100644 --- a/src/components/public/PublicMenuItem.tsx +++ b/src/components/public/PublicMenuItem.tsx @@ -2,8 +2,8 @@ import { useAtom } from 'jotai'; import { PlusIcon, SoupIcon, WineIcon } from 'lucide-react'; +import { orderAtom, type PublicOrderItem } from '~/app/p/[locationSlug]/_state/cart'; import { Badge } from '~/components/ui/badge'; -import { cartAtom, type CartItem } from '~/domain/cart'; import { CURRENCIES, type CurrencyId } from '~/domain/currencies'; import { type MenuItem } from '~/domain/menu-items'; import { MenuModeId } from '~/domain/menu-modes'; @@ -12,11 +12,17 @@ import { toast } from '~/hooks/use-toast'; export function PublicMenuItem(props: { item: MenuItem; currencyId: CurrencyId; menuMode: MenuModeId }) { const { name, description, price, isNew, type } = props.item; const currency = CURRENCIES[props.currencyId]; - const [cart, setCart] = useAtom(cartAtom); + const [, setOrder] = useAtom(orderAtom); const addToOrder = () => { - const initialStatus: CartItem['status'] = 'draft'; - setCart([...cart, { menuItem: props.item, status: initialStatus }]); + const initialStatus: PublicOrderItem['status'] = 'draft'; + setOrder((prevOrder) => { + return { + ...prevOrder, + items: [...prevOrder.items, { menuItem: props.item, status: initialStatus }], + }; + }); + toast({ title: 'Added to cart', description: `${name} has been added to your order.`, diff --git a/src/domain/cart.ts b/src/domain/cart.ts deleted file mode 100644 index cf481f7..0000000 --- a/src/domain/cart.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { atom } from 'jotai'; -import { OrderItemStatus } from '~/domain/order-items'; -import { type MenuItem } from './menu-items'; - -export interface CartItem { - menuItem: Pick; - status: OrderItemStatus; -} - -export const cartAtom = atom([]); diff --git a/src/domain/orders.ts b/src/domain/orders.ts new file mode 100644 index 0000000..a84d66b --- /dev/null +++ b/src/domain/orders.ts @@ -0,0 +1,8 @@ +import { type InferSelectModel } from 'drizzle-orm'; +import { z } from 'zod'; +import { orders } from '~/server/db/schema'; + +export type Order = InferSelectModel; + +export const orderIdSchema = z.coerce.string(); +export type OrderId = z.infer; From 3bfc030637292744966106b673dd4fb9b6bbcc8c Mon Sep 17 00:00:00 2001 From: dopoto Date: Thu, 15 May 2025 11:17:03 +0300 Subject: [PATCH 19/47] add order --- drizzle/0007_tense_may_parker.sql | 17 + drizzle/meta/0007_snapshot.json | 683 ++++++++++++++++++ drizzle/meta/_journal.json | 7 + src/app/actions/placeOrderAction.ts | 138 ++-- .../_components/JotaiProviderWrapper.tsx | 33 + .../_components/PublicFooterPostpaidMode.tsx | 2 +- src/app/p/[locationSlug]/_state/cart.ts | 11 +- src/app/p/[locationSlug]/layout.tsx | 79 +- src/domain/order-items.ts | 7 +- src/domain/orders.ts | 13 + src/server/db/schema.ts | 7 +- src/server/queries/orders.ts | 44 ++ 12 files changed, 945 insertions(+), 96 deletions(-) create mode 100644 drizzle/0007_tense_may_parker.sql create mode 100644 drizzle/meta/0007_snapshot.json create mode 100644 src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx create mode 100644 src/server/queries/orders.ts diff --git a/drizzle/0007_tense_may_parker.sql b/drizzle/0007_tense_may_parker.sql new file mode 100644 index 0000000..008479c --- /dev/null +++ b/drizzle/0007_tense_may_parker.sql @@ -0,0 +1,17 @@ +CREATE TABLE "next-menu_order_item" ( + "id" integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "next-menu_order_item_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "order_id" integer NOT NULL, + "status" varchar(10) DEFAULT 'draft' NOT NULL, + "created_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, + "updated_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "next-menu_order" ( + "id" integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "next-menu_order_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "location_id" integer NOT NULL, + "created_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, + "updated_at" timestamp with time zone +); +--> statement-breakpoint +ALTER TABLE "next-menu_order_item" ADD CONSTRAINT "next-menu_order_item_order_id_next-menu_order_id_fk" FOREIGN KEY ("order_id") REFERENCES "public"."next-menu_order"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "next-menu_order" ADD CONSTRAINT "next-menu_order_location_id_next-menu_location_id_fk" FOREIGN KEY ("location_id") REFERENCES "public"."next-menu_location"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/meta/0007_snapshot.json b/drizzle/meta/0007_snapshot.json new file mode 100644 index 0000000..bec8f4f --- /dev/null +++ b/drizzle/meta/0007_snapshot.json @@ -0,0 +1,683 @@ +{ + "id": "8ea9a1de-7309-4d55-9187-9962b7977fcf", + "prevId": "7d72f078-0b76-405b-aa11-534a049c0d0a", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.next-menu_location": { + "name": "next-menu_location", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "next-menu_location_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "name": { + "name": "name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "currency_id": { + "name": "currency_id", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true, + "default": "'USD'" + }, + "org_id": { + "name": "org_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "menu_mode": { + "name": "menu_mode", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'noninteractive'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "next-menu_location_org_id_next-menu_organization_id_fk": { + "name": "next-menu_location_org_id_next-menu_organization_id_fk", + "tableFrom": "next-menu_location", + "tableTo": "next-menu_organization", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "next-menu_location_name_unique": { + "name": "next-menu_location_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + }, + "next-menu_location_slug_unique": { + "name": "next-menu_location_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.next-menu_menu_item": { + "name": "next-menu_menu_item", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "next-menu_menu_item_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "location_id": { + "name": "location_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "varchar(256)", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "default": "'dish'" + }, + "is_new": { + "name": "is_new", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_published": { + "name": "is_published", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "next-menu_menu_item_location_id_next-menu_location_id_fk": { + "name": "next-menu_menu_item_location_id_next-menu_location_id_fk", + "tableFrom": "next-menu_menu_item", + "tableTo": "next-menu_location", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.next-menu_menu_items_to_menus": { + "name": "next-menu_menu_items_to_menus", + "schema": "", + "columns": { + "menu_id": { + "name": "menu_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "menu_item_id": { + "name": "menu_item_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "sort_order_index": { + "name": "sort_order_index", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "menu_items_to_menus_menu_item_idx": { + "name": "menu_items_to_menus_menu_item_idx", + "columns": [ + { + "expression": "menu_item_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "menu_items_to_menus_menu_idx": { + "name": "menu_items_to_menus_menu_idx", + "columns": [ + { + "expression": "menu_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "next-menu_menu_items_to_menus_menu_id_next-menu_menu_id_fk": { + "name": "next-menu_menu_items_to_menus_menu_id_next-menu_menu_id_fk", + "tableFrom": "next-menu_menu_items_to_menus", + "tableTo": "next-menu_menu", + "columnsFrom": [ + "menu_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "next-menu_menu_items_to_menus_menu_item_id_next-menu_menu_item_id_fk": { + "name": "next-menu_menu_items_to_menus_menu_item_id_next-menu_menu_item_id_fk", + "tableFrom": "next-menu_menu_items_to_menus", + "tableTo": "next-menu_menu_item", + "columnsFrom": [ + "menu_item_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "next-menu_menu_items_to_menus_menu_id_menu_item_id_pk": { + "name": "next-menu_menu_items_to_menus_menu_id_menu_item_id_pk", + "columns": [ + "menu_id", + "menu_item_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.next-menu_menu": { + "name": "next-menu_menu", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "next-menu_menu_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "name": { + "name": "name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": false + }, + "location_id": { + "name": "location_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "is_published": { + "name": "is_published", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "next-menu_menu_location_id_next-menu_location_id_fk": { + "name": "next-menu_menu_location_id_next-menu_location_id_fk", + "tableFrom": "next-menu_menu", + "tableTo": "next-menu_location", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.next-menu_order_item": { + "name": "next-menu_order_item", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "next-menu_order_item_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "order_id": { + "name": "order_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "next-menu_order_item_order_id_next-menu_order_id_fk": { + "name": "next-menu_order_item_order_id_next-menu_order_id_fk", + "tableFrom": "next-menu_order_item", + "tableTo": "next-menu_order", + "columnsFrom": [ + "order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.next-menu_order": { + "name": "next-menu_order", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "next-menu_order_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "location_id": { + "name": "location_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "next-menu_order_location_id_next-menu_location_id_fk": { + "name": "next-menu_order_location_id_next-menu_location_id_fk", + "tableFrom": "next-menu_order", + "tableTo": "next-menu_location", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.next-menu_organization": { + "name": "next-menu_organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "next-menu_organization_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "clerk_org_id": { + "name": "clerk_org_id", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "varchar(256)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "next-menu_organization_clerk_org_id_unique": { + "name": "next-menu_organization_clerk_org_id_unique", + "nullsNotDistinct": false, + "columns": [ + "clerk_org_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.next-menu_user": { + "name": "next-menu_user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "next-menu_user_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "role": { + "name": "role", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "clerk_user_id": { + "name": "clerk_user_id", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "next-menu_user_org_id_next-menu_organization_id_fk": { + "name": "next-menu_user_org_id_next-menu_organization_id_fk", + "tableFrom": "next-menu_user", + "tableTo": "next-menu_organization", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 4f65ed3..8fe33c8 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -50,6 +50,13 @@ "when": 1747135692932, "tag": "0006_gorgeous_roxanne_simpson", "breakpoints": true + }, + { + "idx": 7, + "version": "7", + "when": 1747292885935, + "tag": "0007_tense_may_parker", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/app/actions/placeOrderAction.ts b/src/app/actions/placeOrderAction.ts index 3d7182a..fdfd025 100644 --- a/src/app/actions/placeOrderAction.ts +++ b/src/app/actions/placeOrderAction.ts @@ -1,56 +1,102 @@ 'use server'; -import { PublicOrderItem } from '~/app/p/[locationSlug]/_state/cart'; -import { LocationId } from '~/domain/locations'; -import { OrderId } from '~/domain/orders'; +import * as Sentry from '@sentry/nextjs'; +import { headers } from 'next/headers'; +import type { z } from 'node_modules/zod/lib/external'; +import { menuFormSchema } from '~/domain/menus'; +import { orderFormSchema } from '~/domain/orders'; +import { AppError } from '~/lib/error-utils.server'; +import { type FormState, processFormErrors } from '~/lib/form-state'; +import { createOrder } from '~/server/queries/orders'; /** * Generates a unique order number using a timestamp and random characters */ -function generateUniqueOrderNumber(): string { - const timestamp = new Date().getTime().toString(36).toUpperCase(); - const randomStr = Math.random().toString(36).substring(2, 6).toUpperCase(); - return `ORD-${timestamp}${randomStr}`; -} /** * Places an order by storing it in the database and updating the order items status */ -export async function placeOrderAction( - cartItems: PublicOrderItem[], - locationId: LocationId, -): Promise<{ orderId: OrderId }> { - try { - // Filter only draft items for the order - const draftItems = cartItems.filter((item) => item.status === 'draft'); - - if (draftItems.length === 0) { - throw new Error('No draft items to order'); - } - - // Generate a unique order number - // In a real implementation, this would be stored in the database - const orderId = generateUniqueOrderNumber(); - - console.log(`Creating order ${orderId} for location ${locationId} with ${draftItems.length} items`); - - // Here we would typically: - // 1. Create an order record in the database - // 2. Create order item records for each draft item - // 3. Update the status of those items to 'ordered' - - // Since we don't have direct database access in this example, - // we're just simulating a successful order creation - - // In a real implementation, this would be a database transaction - - // Return the order number so it can be displayed to the user - return { orderId }; - } catch (error) { - console.error('Failed to place order:', error); - if (error instanceof Error) { - throw new Error(error.message); - } - throw new Error('Failed to place order. Please try again.'); - } -} +// export async function placeOrderAction( +// cartItems: PublicOrderItem[], +// locationId: LocationId, +// ): Promise<{ orderId: OrderId }> { +// try { +// // Filter only draft items for the order +// const draftItems = cartItems.filter((item) => item.status === 'draft'); + +// if (draftItems.length === 0) { +// throw new Error('No draft items to order'); +// } + +// // Generate a unique order number +// // In a real implementation, this would be stored in the database +// const orderId = generateUniqueOrderNumber(); + +// console.log(`Creating order ${orderId} for location ${locationId} with ${draftItems.length} items`); + +// // Here we would typically: +// // 1. Create an order record in the database +// // 2. Create order item records for each draft item +// // 3. Update the status of those items to 'ordered' + +// // Since we don't have direct database access in this example, +// // we're just simulating a successful order creation + +// // In a real implementation, this would be a database transaction + +// // Return the order number so it can be displayed to the user +// return { orderId }; +// } catch (error) { +// console.error('Failed to place order:', error); +// if (error instanceof Error) { +// throw new Error(error.message); +// } +// throw new Error('Failed to place order. Please try again.'); +// } +// } + +export const placeOrderAction = async ( + data: z.infer, +): Promise> => { + 'use server'; + return await Sentry.withServerActionInstrumentation( + 'placeOrderAction', + { + headers: headers(), + recordResponse: true, + }, + async () => { + try { + const parsedForm = orderFormSchema.safeParse(data); + if (!parsedForm.success) { + return processFormErrors(parsedForm.error, data); + } + + const availableQuota = 1; // TODO = await getAvailableFeatureQuota('menus'); + if (availableQuota <= 0) { + return { + status: 'error' as const, + rootError: 'Out of quota for orders.', + }; + } + const order = await createOrder(parsedForm.data); + + // TODO: revalidatePath(ROUTES.menus(parsedForm.data.locationId)); + // TODO revalidate public path + return { status: 'success' as const, orderId: order.id }; + } catch (error) { + if (error instanceof AppError) { + return { + status: 'error' as const, + rootError: error.publicMessage, + }; + } else { + return { + status: 'error' as const, + rootError: 'An error occurred while processing the order.', + }; + } + } + }, + ); +}; diff --git a/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx b/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx new file mode 100644 index 0000000..740b149 --- /dev/null +++ b/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx @@ -0,0 +1,33 @@ +// app/your-page/JotaiProviderWrapper.tsx +'use client'; + +import { Provider, useSetAtom } from 'jotai'; +import 'jotai-devtools/styles.css'; +import { ReactNode, useEffect } from 'react'; +import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; +import { LocationId } from '~/domain/locations'; + +function Initializer(props: { locationId: LocationId }) { + const setOrder = useSetAtom(orderAtom); + + useEffect(() => { + setOrder({ locationId: props.locationId, items: [] }); + }, [props.locationId, setOrder]); + + return null; +} + +export default function JotaiProviderWrapper({ + children, + locationId, +}: { + children: ReactNode; + locationId: LocationId; +}) { + return ( + + + {children} + + ); +} diff --git a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx index 5313bea..4828f24 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx @@ -36,7 +36,7 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati try { setIsLoading(true); - const { orderId } = await placeOrderAction(order.items, props.locationId); + const { orderId } = await placeOrderAction(order); setOrder((prevOrder) => { return { diff --git a/src/app/p/[locationSlug]/_state/cart.ts b/src/app/p/[locationSlug]/_state/cart.ts index 6a36319..e360077 100644 --- a/src/app/p/[locationSlug]/_state/cart.ts +++ b/src/app/p/[locationSlug]/_state/cart.ts @@ -1,16 +1,15 @@ import { atom } from 'jotai'; +import { z } from 'zod'; import { OrderItemStatus } from '~/domain/order-items'; -import { OrderId } from '~/domain/orders'; +import { orderFormSchema } from '~/domain/orders'; import { type MenuItem } from '../../../../domain/menu-items'; -export interface PublicOrder { - orderId: OrderId | null; - items: PublicOrderItem[]; -} +export type PublicOrder = z.infer; export interface PublicOrderItem { menuItem: Pick; status: OrderItemStatus; } -export const orderAtom = atom({ orderId: null, items: [] }); +export const orderAtom = atom({ locationId: 0, items: [] }); +orderAtom.debugLabel = 'orderAtom'; diff --git a/src/app/p/[locationSlug]/layout.tsx b/src/app/p/[locationSlug]/layout.tsx index d4c2e83..e3f79d4 100644 --- a/src/app/p/[locationSlug]/layout.tsx +++ b/src/app/p/[locationSlug]/layout.tsx @@ -1,6 +1,7 @@ import { cookies } from 'next/headers'; import Image from 'next/image'; import { PostHog } from 'posthog-node'; +import JotaiProviderWrapper from '~/app/p/[locationSlug]/_components/JotaiProviderWrapper'; import { PublicFooterPostpaidMode } from '~/app/p/[locationSlug]/_components/PublicFooterPostpaidMode'; import { PublicFooterPrepaidMode } from '~/app/p/[locationSlug]/_components/PublicFooterPrepaidMode'; import { AnalyticsEventSender } from '~/components/AnalyticsEventSender'; @@ -47,49 +48,53 @@ export default async function Layout({ params, children }: { params: Params; chi }); return ( -
-
-
-
- Hero banner -
-
-
+ +
+
+
+
Logo
+
+
+ Logo +
+
+
+
+

{location.name}

-
-
-

{location.name}

-
-
+ -
{children}
+
{children}
- {location.menuMode === 'prepaid' && } - {location.menuMode === 'postpaid' && ( - - )} + {location.menuMode === 'prepaid' && ( + + )} + {location.menuMode === 'postpaid' && ( + + )} - -
+ +
+ ); } diff --git a/src/domain/order-items.ts b/src/domain/order-items.ts index 00a714e..a48c7ed 100644 --- a/src/domain/order-items.ts +++ b/src/domain/order-items.ts @@ -1,9 +1,8 @@ -export const ORDER_ONLY_STATUSES = ['draft', 'ordered', 'delivered', 'paid'] as const; export const PREPAID_STATUSES = ['draft', 'paid'] as const; +export const POSTPAID_STATUSES = ['draft', 'ordered', 'delivered', 'paid'] as const; +export const ORDER_ITEM_STATUSES = [...new Set([...POSTPAID_STATUSES, ...PREPAID_STATUSES])] as const; -export const ORDER_ITEM_STATUSES = [...new Set([...ORDER_ONLY_STATUSES, ...PREPAID_STATUSES])] as const; - -type PostpaidOrderItemStatus = (typeof ORDER_ONLY_STATUSES)[number]; +type PostpaidOrderItemStatus = (typeof POSTPAID_STATUSES)[number]; type PrepaidOrderItemStatus = (typeof PREPAID_STATUSES)[number]; export type OrderItemStatus = PostpaidOrderItemStatus | PrepaidOrderItemStatus; diff --git a/src/domain/orders.ts b/src/domain/orders.ts index a84d66b..6e2d40a 100644 --- a/src/domain/orders.ts +++ b/src/domain/orders.ts @@ -1,8 +1,21 @@ import { type InferSelectModel } from 'drizzle-orm'; import { z } from 'zod'; +import { PublicOrderItem } from '~/app/p/[locationSlug]/_state/cart'; import { orders } from '~/server/db/schema'; +export const PREPAID_STATUSES = ['draft', 'paid'] as const; + export type Order = InferSelectModel; export const orderIdSchema = z.coerce.string(); export type OrderId = z.infer; + +export const orderFormSchema = z.object({ + orderId: z.string().optional(), + locationId: z + .number({ + required_error: 'Location ID is required', + }) + .min(0, 'Location Id must be positive'), + items: z.array(z.custom()), +}); diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts index 299d55a..19970f4 100644 --- a/src/server/db/schema.ts +++ b/src/server/db/schema.ts @@ -150,7 +150,7 @@ export const orders = createTable('order', { updatedAt: timestamp('updated_at', { withTimezone: true }).$onUpdate(() => new Date()), }); -const defaultOrderStatus: OrderItemStatus = 'draft'; +const defaultOrderItemStatus: OrderItemStatus = 'draft'; export const orderItems = createTable( 'order_item', @@ -159,7 +159,10 @@ export const orderItems = createTable( orderId: integer('order_id') .notNull() .references(() => orders.id), - status: varchar('status', { length: 10 }).notNull().default(defaultOrderStatus), + menuItemId: integer('menu_item_id') + .notNull() + .references(() => menuItems.id), + status: varchar('status', { length: 10 }).notNull().default(defaultOrderItemStatus), createdAt: timestamp('created_at', { withTimezone: true }) .default(sql`CURRENT_TIMESTAMP`) .notNull(), diff --git a/src/server/queries/orders.ts b/src/server/queries/orders.ts new file mode 100644 index 0000000..6286e8b --- /dev/null +++ b/src/server/queries/orders.ts @@ -0,0 +1,44 @@ +import { sql } from 'drizzle-orm'; +import { z } from 'zod'; +import { orderFormSchema } from '~/domain/orders'; +import { AppError } from '~/lib/error-utils.server'; +import { db } from '~/server/db'; +import { orderItems, orders } from '~/server/db/schema'; + +// function generateUniqueOrderNumber(): string { +// const timestamp = new Date().getTime().toString(36).toUpperCase(); +// const randomStr = Math.random().toString(36).substring(2, 6).toUpperCase(); +// return `ORD-${timestamp}${randomStr}`; +// } + +export async function createOrder(data: z.infer) { + return await db.transaction(async (tx) => { + const [order] = await tx + .insert(orders) + .values({ + locationId: data.locationId, + createdAt: sql`CURRENT_TIMESTAMP`, + updatedAt: sql`CURRENT_TIMESTAMP`, + }) + .returning(); + + if (!order) { + throw new AppError({ internalMessage: 'Could not insert order' }); + } + + if (data.items) { + for (let i = 0; i < data.items.length; i++) { + const item = data.items[i]; + await tx.insert(orderItems).values({ + orderId: order.id, + menuItemId: item!.menuItem.id, + status: 'draft', + createdAt: sql`CURRENT_TIMESTAMP`, + updatedAt: sql`CURRENT_TIMESTAMP`, + }); + } + } + + return order; + }); +} From 8fc11c71cb8a95b06f31c477389fbeafeae663b1 Mon Sep 17 00:00:00 2001 From: dopoto Date: Thu, 15 May 2025 12:03:17 +0300 Subject: [PATCH 20/47] o items --- package.json | 18 +- pnpm-lock.yaml | 1002 +++++++++++------ .../_components/OrderItemsList.tsx | 23 + .../_components/PublicFooterDrawer.tsx | 6 +- .../_components/PublicFooterPostpaidMode.tsx | 101 +- src/lib/form-state.ts | 1 + src/server/queries/orders.ts | 4 +- 7 files changed, 732 insertions(+), 423 deletions(-) create mode 100644 src/app/p/[locationSlug]/_components/OrderItemsList.tsx diff --git a/package.json b/package.json index c29df52..096deb7 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,8 @@ "knip": "knip" }, "dependencies": { - "@clerk/nextjs": "^6.19.2", - "@clerk/themes": "2.2.43", + "@clerk/nextjs": "^6.19.3", + "@clerk/themes": "2.2.44", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", @@ -50,7 +50,7 @@ "@radix-ui/react-tabs": "^1.1.11", "@radix-ui/react-toast": "^1.2.13", "@radix-ui/react-tooltip": "^1.2.6", - "@sentry/nextjs": "9.17.0", + "@sentry/nextjs": "9.19.0", "@stripe/react-stripe-js": "^3.7.0", "@stripe/stripe-js": "^7.3.0", "@t3-oss/env-nextjs": "^0.13.4", @@ -67,7 +67,7 @@ "lucide-react": "^0.510.0", "next": "15.3.2", "postgres": "^3.4.5", - "posthog-js": "^1.240.6", + "posthog-js": "^1.242.1", "posthog-node": "^4.17.1", "react": "^19.1.0", "react-confetti": "6.4.0", @@ -100,9 +100,9 @@ "@testing-library/react": "^16.3.0", "@types/eslint": "^9.6.1", "@types/jest": "^29.5.14", - "@types/node": "^22.15.17", + "@types/node": "^22.15.18", "@types/react": "^19.1.4", - "@types/react-dom": "^19.1.4", + "@types/react-dom": "^19.1.5", "cross-env": "^7.0.3", "drizzle-kit": "^0.31.1", "eslint": "9.26.0", @@ -113,17 +113,17 @@ "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "jotai-devtools": "^0.12.0", - "knip": "^5.55.1", + "knip": "^5.56.0", "postcss": "^8.5.3", "prettier": "^3.5.3", "prettier-plugin-organize-imports": "4.1.0", "prettier-plugin-tailwindcss": "^0.6.11", "storybook": "8.6.12", "tailwindcss": "^4.1.6", - "ts-jest": "^29.3.2", + "ts-jest": "^29.3.3", "ts-node": "^10.9.2", "typescript": "^5.8.3", - "typescript-eslint": "^8.32.0" + "typescript-eslint": "^8.32.1" }, "ct3aMetadata": { "initVersion": "7.38.1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3a53344..bc2a12a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,11 +9,11 @@ importers: .: dependencies: '@clerk/nextjs': - specifier: ^6.19.2 - version: 6.19.2(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + specifier: ^6.19.3 + version: 6.19.3(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@clerk/themes': - specifier: 2.2.43 - version: 2.2.43 + specifier: 2.2.44 + version: 2.2.44 '@dnd-kit/core': specifier: ^6.3.1 version: 6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -34,52 +34,52 @@ importers: version: 15.3.2(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) '@radix-ui/react-avatar': specifier: ^1.1.9 - version: 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-checkbox': specifier: ^1.3.1 - version: 1.3.1(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.3.1(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-collapsible': specifier: ^1.1.10 - version: 1.1.10(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-dialog': specifier: ^1.1.13 - version: 1.1.13(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.13(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-dropdown-menu': specifier: ^2.1.14 - version: 2.1.14(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.1.14(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-label': specifier: ^2.1.6 - version: 2.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-popover': specifier: ^1.1.13 - version: 1.1.13(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.13(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-progress': specifier: ^1.1.6 - version: 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-radio-group': specifier: ^1.3.6 - version: 1.3.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.3.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-separator': specifier: ^1.1.6 - version: 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': specifier: ^1.2.2 version: 1.2.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-switch': specifier: ^1.2.4 - version: 1.2.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-tabs': specifier: ^1.1.11 - version: 1.1.11(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.11(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-toast': specifier: ^1.2.13 - version: 1.2.13(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.13(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-tooltip': specifier: ^1.2.6 - version: 1.2.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@sentry/nextjs': - specifier: 9.17.0 - version: 9.17.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.98.0(esbuild@0.25.2)) + specifier: 9.19.0 + version: 9.19.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.98.0(esbuild@0.25.2)) '@stripe/react-stripe-js': specifier: ^3.7.0 version: 3.7.0(@stripe/stripe-js@7.3.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -129,8 +129,8 @@ importers: specifier: ^3.4.5 version: 3.4.5 posthog-js: - specifier: ^1.240.6 - version: 1.240.6 + specifier: ^1.242.1 + version: 1.242.1 posthog-node: specifier: ^4.17.1 version: 4.17.1 @@ -163,7 +163,7 @@ importers: version: 0.0.1 stripe: specifier: ^18.1.0 - version: 18.1.0(@types/node@22.15.17) + version: 18.1.0(@types/node@22.15.18) tailwind-merge: specifier: ^3.3.0 version: 3.3.0 @@ -175,7 +175,7 @@ importers: version: 2.0.1 vaul: specifier: ^1.1.2 - version: 1.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) zod: specifier: 3.24.4 version: 3.24.4 @@ -197,7 +197,7 @@ importers: version: 8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) '@storybook/nextjs': specifier: 8.6.12 - version: 8.6.12(babel-plugin-macros@3.1.0)(esbuild@0.25.2)(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(type-fest@4.40.0)(typescript@5.8.3)(webpack-hot-middleware@2.26.1)(webpack@5.98.0(esbuild@0.25.2)) + version: 8.6.12(babel-plugin-macros@3.1.0)(esbuild@0.25.2)(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(type-fest@4.41.0)(typescript@5.8.3)(webpack-hot-middleware@2.26.1)(webpack@5.98.0(esbuild@0.25.2)) '@storybook/react': specifier: 8.6.12 version: 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3) @@ -215,7 +215,7 @@ importers: version: 6.6.3 '@testing-library/react': specifier: ^16.3.0 - version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@types/eslint': specifier: ^9.6.1 version: 9.6.1 @@ -223,14 +223,14 @@ importers: specifier: ^29.5.14 version: 29.5.14 '@types/node': - specifier: ^22.15.17 - version: 22.15.17 + specifier: ^22.15.18 + version: 22.15.18 '@types/react': specifier: ^19.1.4 version: 19.1.4 '@types/react-dom': - specifier: ^19.1.4 - version: 19.1.4(@types/react@19.1.4) + specifier: ^19.1.5 + version: 19.1.5(@types/react@19.1.4) cross-env: specifier: ^7.0.3 version: 7.0.3 @@ -254,7 +254,7 @@ importers: version: 0.12.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@22.15.17)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3)) + version: 29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) jest-environment-jsdom: specifier: ^29.7.0 version: 29.7.0(bufferutil@4.0.9) @@ -262,8 +262,8 @@ importers: specifier: ^0.12.0 version: 0.12.0(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(redux@5.0.1)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) knip: - specifier: ^5.55.1 - version: 5.55.1(@types/node@22.15.17)(typescript@5.8.3) + specifier: ^5.56.0 + version: 5.56.0(@types/node@22.15.18)(typescript@5.8.3) postcss: specifier: ^8.5.3 version: 8.5.3 @@ -283,17 +283,17 @@ importers: specifier: ^4.1.6 version: 4.1.6 ts-jest: - specifier: ^29.3.2 - version: 29.3.2(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.9))(esbuild@0.25.2)(jest@29.7.0(@types/node@22.15.17)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3)))(typescript@5.8.3) + specifier: ^29.3.3 + version: 29.3.3(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.9))(esbuild@0.25.2)(jest@29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)))(typescript@5.8.3) ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@22.15.17)(typescript@5.8.3) + version: 10.9.2(@types/node@22.15.18)(typescript@5.8.3) typescript: specifier: ^5.8.3 version: 5.8.3 typescript-eslint: - specifier: ^8.32.0 - version: 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + specifier: ^8.32.1 + version: 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) packages: @@ -946,8 +946,8 @@ packages: peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@clerk/backend@1.32.0': - resolution: {integrity: sha512-D0hC2mnU79wM4q8GU5ZFrMD3p9lHy3aM+qDi9WqD3L40d65JJ+lzD+rKSqfM5/gDtUWUdD2Ezvqa0ZObRXTcgg==} + '@clerk/backend@1.32.1': + resolution: {integrity: sha512-Y7Txq7L3JPilb4kreHpp+3z4HUta+A2LdcurayoUEQyIzBH93AXnWnuRX1EFgSaQ6THsUq5IwmhemETLitNdKg==} engines: {node: '>=18.17.0'} peerDependencies: svix: ^1.62.0 @@ -955,23 +955,23 @@ packages: svix: optional: true - '@clerk/clerk-react@5.31.2': - resolution: {integrity: sha512-uMclkpxhZ0yE9VZ9AIRDqAb4y5qodImEbTwBHvoT1261eAKoB38nanUEgBKVaIm1N/Z/e6KTUjGYkVHJ5KmE7A==} + '@clerk/clerk-react@5.31.3': + resolution: {integrity: sha512-EzswkUMsWVrLbnE12+gD7W05jTPpfnHVhC7TjWHaZphLxhY+8Tt8xHfg5Rhj52/d4vV+BXE12o6u1lFo8mls7w==} engines: {node: '>=18.17.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 - '@clerk/nextjs@6.19.2': - resolution: {integrity: sha512-GW4ZNVlc7hj4klHo9Qy5zsvNjZqkwbTwZ1AvmsYNCj9yKLBdM+yOZe3DDkZMKcfhian/WF5ERG1JyERr6BxNaA==} + '@clerk/nextjs@6.19.3': + resolution: {integrity: sha512-bpwzbUajkFqGGmhRn95Pp/1NoDvqniZKSbVbXHHdzDqAB9QjLAjoHLgyehEdAn/2nkiIjqk2PKH3Kf3CiXsH4Q==} engines: {node: '>=18.17.0'} peerDependencies: next: ^13.5.7 || ^14.2.25 || ^15.2.3 react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 - '@clerk/shared@3.8.2': - resolution: {integrity: sha512-y8g8/wbFo/3gCRsrk2rmGQ1uzIb8lPM4Wjggt4glC8mPqTGgMFuIAK0Sj0PAJOq5739sokFXc4CzZdMnu/P5ug==} + '@clerk/shared@3.9.0': + resolution: {integrity: sha512-M+2IwE8DUof7RAjo0iCNlr4pRuXo5g8rDvk7Q1L9xVFSh7N9aA87BX1ctmUbus/BKrCSDLRiYQ6V0uGATg9iNQ==} engines: {node: '>=18.17.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 @@ -982,12 +982,12 @@ packages: react-dom: optional: true - '@clerk/themes@2.2.43': - resolution: {integrity: sha512-BH7Q2Zbgz1DhVUjVjm/sKgwdHJDymLLiieBwmWIbWyMjJaORXJZVqlxICMRUeyRD9sO8zY6XaA8rgvN1FR0F5w==} + '@clerk/themes@2.2.44': + resolution: {integrity: sha512-jKVICOjaJOm7uE3g/K/wRWJ3MY9iBDZ6ua5vO+qnU1poTohKC7QJYX4tY9vmAJ8B/K/G3PGXnhXzA1dEF66ERQ==} engines: {node: '>=18.17.0'} - '@clerk/types@4.58.0': - resolution: {integrity: sha512-P30Vnqaw2UzoSZivNG61NvF9WiAVbKItOvkVJzUbwqDkISdmNOio8mWCeFuKq+bxjPESvS+S615Uplm7dl6jpg==} + '@clerk/types@4.58.1': + resolution: {integrity: sha512-bs1jQF4Nxw4a34q8TZEqY2Jyuj8kcMgYHUDCP060XkdvKWUqbQZW7Hb3YBrxTqLFLawku9cWpgP3/41Os1584g==} engines: {node: '>=18.17.0'} '@cspotcode/source-map-support@0.8.1': @@ -1019,12 +1019,18 @@ packages: '@drizzle-team/brocli@0.10.2': resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} + '@emnapi/core@1.4.3': + resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} + '@emnapi/runtime@1.3.1': resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} '@emnapi/runtime@1.4.0': resolution: {integrity: sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==} + '@emnapi/wasi-threads@1.0.2': + resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==} + '@emotion/babel-plugin@11.13.5': resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} @@ -1400,8 +1406,9 @@ packages: resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@fastify/otel@0.6.0': - resolution: {integrity: sha512-lL+36KwGcFiAMcsPOLLsR+GV8ZpQuz5RLVstlgqmecTdQLTXVOe9Z8uwpMg9ktPcV++Ugp3dzzpBKNFWWWelYg==} + '@fastify/otel@https://codeload.github.com/getsentry/fastify-otel/tar.gz/ae3088d65e286bdc94ac5d722573537d6a6671bb': + resolution: {tarball: https://codeload.github.com/getsentry/fastify-otel/tar.gz/ae3088d65e286bdc94ac5d722573537d6a6671bb} + version: 0.8.0 peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -1798,6 +1805,9 @@ packages: resolution: {integrity: sha512-k/1pb70eD638anoi0e8wUGAlbMJXyvdV4p62Ko+EZ7eBe1xMx8Uhak1R5DgfoofsK5IBBnRwsYGTaLZl+6/+RQ==} engines: {node: '>=18'} + '@napi-rs/wasm-runtime@0.2.9': + resolution: {integrity: sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==} + '@neondatabase/serverless@0.9.5': resolution: {integrity: sha512-siFas6gItqv6wD/pZnvdu34wEqgG3nSE6zWZdq5j2DEsa+VvX8i/5HXJOo06qrw5axPXn+lGCxeR+NLaSPIXug==} @@ -1877,10 +1887,6 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} - '@opentelemetry/api-logs@0.200.0': - resolution: {integrity: sha512-IKJBQxh91qJ+3ssRly5hYEJ8NDHu9oY/B1PXVSCWf7zytmYO9RNLB0Ox9XQ/fJ8m6gY6Q6NtBWlmXfaXt5Uc4Q==} - engines: {node: '>=8.0.0'} - '@opentelemetry/api-logs@0.57.2': resolution: {integrity: sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==} engines: {node: '>=14'} @@ -1901,12 +1907,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.0.0': - resolution: {integrity: sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/instrumentation-amqplib@0.46.1': resolution: {integrity: sha512-AyXVnlCf/xV3K/rNumzKxZqsULyITJH6OVLiW6730JPRqWA7Zc9bvYoVNpN6iOpTU8CasH34SU/ksVJmObFibQ==} engines: {node: '>=14'} @@ -2039,12 +2039,6 @@ packages: peerDependencies: '@opentelemetry/api': ^1.7.0 - '@opentelemetry/instrumentation@0.200.0': - resolution: {integrity: sha512-pmPlzfJd+vvgaZd/reMsC8RWgTXn2WY1OWT5RT42m3aOn5532TozwXNDhg1vzqJ+jnvmkREcdLr27ebJEQt0Jg==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.57.2': resolution: {integrity: sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==} engines: {node: '>=14'} @@ -2081,6 +2075,71 @@ packages: peerDependencies: '@opentelemetry/api': ^1.1.0 + '@oxc-resolver/binding-darwin-arm64@9.0.2': + resolution: {integrity: sha512-MVyRgP2gzJJtAowjG/cHN3VQXwNLWnY+FpOEsyvDepJki1SdAX/8XDijM1yN6ESD1kr9uhBKjGelC6h3qtT+rA==} + cpu: [arm64] + os: [darwin] + + '@oxc-resolver/binding-darwin-x64@9.0.2': + resolution: {integrity: sha512-7kV0EOFEZ3sk5Hjy4+bfA6XOQpCwbDiDkkHN4BHHyrBHsXxUR05EcEJPPL1WjItefg+9+8hrBmoK0xRoDs41+A==} + cpu: [x64] + os: [darwin] + + '@oxc-resolver/binding-freebsd-x64@9.0.2': + resolution: {integrity: sha512-6OvkEtRXrt8sJ4aVfxHRikjain9nV1clIsWtJ1J3J8NG1ZhjyJFgT00SCvqxbK+pzeWJq6XzHyTCN78ML+lY2w==} + cpu: [x64] + os: [freebsd] + + '@oxc-resolver/binding-linux-arm-gnueabihf@9.0.2': + resolution: {integrity: sha512-aYpNL6o5IRAUIdoweW21TyLt54Hy/ZS9tvzNzF6ya1ckOQ8DLaGVPjGpmzxdNja9j/bbV6aIzBH7lNcBtiOTkQ==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-gnu@9.0.2': + resolution: {integrity: sha512-RGFW4vCfKMFEIzb9VCY0oWyyY9tR1/o+wDdNePhiUXZU4SVniRPQaZ1SJ0sUFI1k25pXZmzQmIP6cBmazi/Dew==} + cpu: [arm64] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-musl@9.0.2': + resolution: {integrity: sha512-lxx/PibBfzqYvut2Y8N2D0Ritg9H8pKO+7NUSJb9YjR/bfk2KRmP8iaUz3zB0JhPtf/W3REs65oKpWxgflGToA==} + cpu: [arm64] + os: [linux] + + '@oxc-resolver/binding-linux-riscv64-gnu@9.0.2': + resolution: {integrity: sha512-yD28ptS/OuNhwkpXRPNf+/FvrO7lwURLsEbRVcL1kIE0GxNJNMtKgIE4xQvtKDzkhk6ZRpLho5VSrkkF+3ARTQ==} + cpu: [riscv64] + os: [linux] + + '@oxc-resolver/binding-linux-s390x-gnu@9.0.2': + resolution: {integrity: sha512-WBwEJdspoga2w+aly6JVZeHnxuPVuztw3fPfWrei2P6rNM5hcKxBGWKKT6zO1fPMCB4sdDkFohGKkMHVV1eryQ==} + cpu: [s390x] + os: [linux] + + '@oxc-resolver/binding-linux-x64-gnu@9.0.2': + resolution: {integrity: sha512-a2z3/cbOOTUq0UTBG8f3EO/usFcdwwXnCejfXv42HmV/G8GjrT4fp5+5mVDoMByH3Ce3iVPxj1LmS6OvItKMYQ==} + cpu: [x64] + os: [linux] + + '@oxc-resolver/binding-linux-x64-musl@9.0.2': + resolution: {integrity: sha512-bHZF+WShYQWpuswB9fyxcgMIWVk4sZQT0wnwpnZgQuvGTZLkYJ1JTCXJMtaX5mIFHf69ngvawnwPIUA4Feil0g==} + cpu: [x64] + os: [linux] + + '@oxc-resolver/binding-wasm32-wasi@9.0.2': + resolution: {integrity: sha512-I5cSgCCh5nFozGSHz+PjIOfrqW99eUszlxKLgoNNzQ1xQ2ou9ZJGzcZ94BHsM9SpyYHLtgHljmOZxCT9bgxYNA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-resolver/binding-win32-arm64-msvc@9.0.2': + resolution: {integrity: sha512-5IhoOpPr38YWDWRCA5kP30xlUxbIJyLAEsAK7EMyUgqygBHEYLkElaKGgS0X5jRXUQ6l5yNxuW73caogb2FYaw==} + cpu: [arm64] + os: [win32] + + '@oxc-resolver/binding-win32-x64-msvc@9.0.2': + resolution: {integrity: sha512-Qc40GDkaad9rZksSQr2l/V9UubigIHsW69g94Gswc2sKYB3XfJXfIfyV8WTJ67u6ZMXsZ7BH1msSC6Aen75mCg==} + cpu: [x64] + os: [win32] + '@petamoriken/float16@3.9.1': resolution: {integrity: sha512-j+ejhYwY6PeB+v1kn7lZFACUIG97u90WxMuGosILFsl9d4Ovi0sjk0GlPfoEcx+FzvXZDAfioD+NGnnPamXgMA==} @@ -2705,28 +2764,28 @@ packages: '@rushstack/eslint-patch@1.10.5': resolution: {integrity: sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==} - '@sentry-internal/browser-utils@9.17.0': - resolution: {integrity: sha512-37n6NXtkUfdK7YiP3L5DJvhA/iusOmnjHQdX1e2VwI6a29xHCl/vRqLR3XNr5K4m+49al+3fWo2ltcKsfV+0xw==} + '@sentry-internal/browser-utils@9.19.0': + resolution: {integrity: sha512-DlEHX4eIHe5yIuh/cFu9OiaFuk1CTnFK95zj61I7Q2fxmN43dIwC3xAAGJ/Hy+GDQi7kU+BiS2sudSHSTq81BA==} engines: {node: '>=18'} - '@sentry-internal/feedback@9.17.0': - resolution: {integrity: sha512-C2jBlGgYVGm8eXK38wlYQyd6NsHKaQlENg5fx8TDFMKWMNmLf6BmnPZ+y73OsFwcUtBz04CwZteybYB2GgYrvQ==} + '@sentry-internal/feedback@9.19.0': + resolution: {integrity: sha512-yixRrv4NfpjhFW56AuUTjVwZlignB9FWAXXyrmRP3SsFeJCFrAsSD8HOxV9RXNr9ePYl7MEU0Agi43YWhJsiAw==} engines: {node: '>=18'} - '@sentry-internal/replay-canvas@9.17.0': - resolution: {integrity: sha512-w9AxBJIa+MbxDngvwnqouoJ/ezb7wNjxzFXtmaVtGp7hbC4yme/TOTNtFYg2J/ceQf3GMc8AfW5tsP6zU0R7gg==} + '@sentry-internal/replay-canvas@9.19.0': + resolution: {integrity: sha512-YC8yrOjuKSfQgGniJnzkdbFsWEPTlNpzeeYPTxS4ouH1FwfGrSkPmcddjor2YHaLfiuHHqQ/Vvq70n+zruJH7A==} engines: {node: '>=18'} - '@sentry-internal/replay@9.17.0': - resolution: {integrity: sha512-oH4NolXkEpe73eRP9r3K6WpERYItZisYQudsNrtkUBQL5M/uENiE7YTOvL5osD8AWmU0hCKY3Oua+qDi2lB+8g==} + '@sentry-internal/replay@9.19.0': + resolution: {integrity: sha512-i/X9brRchbAF25yjxLTI7E8eoESRPBgIyQOWoWRXXt2n51iBRTjLXSaEfGvjdN+qrMq/yd6nC1/UqJVxXHeIhA==} engines: {node: '>=18'} '@sentry/babel-plugin-component-annotate@3.3.1': resolution: {integrity: sha512-5GOxGT7lZN+I8A7Vp0rWY+726FDKEw8HnFiebe51rQrMbfGfCu2Aw9uSM0nT9OG6xhV6WvGccIcCszTPs4fUZQ==} engines: {node: '>= 14'} - '@sentry/browser@9.17.0': - resolution: {integrity: sha512-3e/Q5bv06Q+XYV2cKmUgfMfnJtBY8MZKufpcwQ2ab2eMrastqau9KjYeWXapskDm179oPLfzLcDCSlDSTcvqpQ==} + '@sentry/browser@9.19.0': + resolution: {integrity: sha512-efKfPQ0yQkdIkC7qJ5TIHxnecLNENGUYl1YD/TC8yyzW2JRf/3OYo5yg1hY2rhsP5RwQShXlT7uA03ABVIkA4A==} engines: {node: '>=18'} '@sentry/bundler-plugin-core@3.3.1': @@ -2779,39 +2838,39 @@ packages: engines: {node: '>= 10'} hasBin: true - '@sentry/core@9.17.0': - resolution: {integrity: sha512-9f1A93/kY9lLH06L1thPx94IhyLjEP3aRxYAtjtBfzId8UtubSpwP92sbxgslodD73R4tURwWJj7nYZ9HLYBUg==} + '@sentry/core@9.19.0': + resolution: {integrity: sha512-I41rKpMJHHZb0z0Nja+Lxto6IkEEmX3uWjnECypF8Z1HIjeJB0+PXl8p/7TeaKYqw2J2GYcRTg7jQZDmvKle1w==} engines: {node: '>=18'} - '@sentry/nextjs@9.17.0': - resolution: {integrity: sha512-BTTUnHt2sFMQ081Fz+00JCiyHHXbujNFkezSeB5EoNIat7VErWh3fGJFjxv34ICfidscGQGeEHbbkyixb29J4w==} + '@sentry/nextjs@9.19.0': + resolution: {integrity: sha512-VE8xCIHaJBNF7DdiaG3MhBvUn5EWSJyCrrbtEkY5cvlo5pc19tBaxDwGJQfK63Z0DuZCuG9lwih7jCdmCXziwA==} engines: {node: '>=18'} peerDependencies: next: ^13.2.0 || ^14.0 || ^15.0.0-rc.0 - '@sentry/node@9.17.0': - resolution: {integrity: sha512-TQkQOEjf4kww15mtt6KJ0s/5+ZNt4zjR0grH3zbuBbp2jca4vbR4/Z8alFsnJv5Rp0ppBfxoNyn9JHKbquvPYw==} + '@sentry/node@9.19.0': + resolution: {integrity: sha512-WKVcUBy5Zc+LGvfV/CfGPBDfnmEOSxLCMYzXIhx0gUxf2+8WpMMc/8yW/25zbXMo3eC4oST4GBDSpTfNdMBz1w==} engines: {node: '>=18'} - '@sentry/opentelemetry@9.17.0': - resolution: {integrity: sha512-hrQ5SwW3pUwQ+ORBILqQqFj9ZLoKqo12OWuTwR8fxCErVJGeezuNvIpyNfkdtEW7X6N0AQnEfAg3umDAt/TAGw==} + '@sentry/opentelemetry@9.19.0': + resolution: {integrity: sha512-Js6153kW5mNjjukk6TVb04D/8DDhA9MO++WRzXWzNP+FiPi5zwtvm+Je2TvTeAjSH74f6o2JpfECdrfPYHWopA==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 - '@opentelemetry/context-async-hooks': ^1.30.1 - '@opentelemetry/core': ^1.30.1 - '@opentelemetry/instrumentation': ^0.57.1 - '@opentelemetry/sdk-trace-base': ^1.30.1 - '@opentelemetry/semantic-conventions': ^1.28.0 - - '@sentry/react@9.17.0': - resolution: {integrity: sha512-hJOVUheFoUKr5e4vHxyKiu72FRgqmMTFIUG9myim8PH8mJYDqab7Z7cOt4dsBR86soKanaRB5PJq5jGFuipLfg==} + '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.0.0 + '@opentelemetry/core': ^1.30.1 || ^2.0.0 + '@opentelemetry/instrumentation': ^0.57.1 || ^0.200.0 + '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.0.0 + '@opentelemetry/semantic-conventions': ^1.30.0 + + '@sentry/react@9.19.0': + resolution: {integrity: sha512-tHuzPVbqKsONlFQsy7FqqGjBaujQoLRIDBLlPPMNoiGvP3rodBl6t1v5zoNAq4m47i3MhvpLEYf6C00j1w5UMQ==} engines: {node: '>=18'} peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x - '@sentry/vercel-edge@9.17.0': - resolution: {integrity: sha512-nNmID71yR71tYh0yhHeLFVuF3p+ANq4LejoOeCYlfRukJdO4sVTR7x3L5iZl75wf4K5o7mvr1xXmRfMn7Ck6dg==} + '@sentry/vercel-edge@9.19.0': + resolution: {integrity: sha512-oPWFCYMYvzaEyyR/HDS42RniHoQA+p32UysAa6yW3HrTs5139d41R8+qaX1TUVKlcNVVTePajCz/V37fBBaPpA==} engines: {node: '>=18'} '@sentry/webpack-plugin@3.3.1': @@ -3208,6 +3267,9 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@tybys/wasm-util@0.9.0': + resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} + '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -3280,8 +3342,8 @@ packages: '@types/mysql@2.15.26': resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} - '@types/node@22.15.17': - resolution: {integrity: sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==} + '@types/node@22.15.18': + resolution: {integrity: sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==} '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -3295,8 +3357,8 @@ packages: '@types/pg@8.6.1': resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} - '@types/react-dom@19.1.4': - resolution: {integrity: sha512-WxYAszDYgsMV31OVyoG4jbAgJI1Gw0Xq9V19zwhy6+hUUJlJIdZ3r/cbdmTqFv++SktQkZ/X+46yGFxp5XJBEg==} + '@types/react-dom@19.1.5': + resolution: {integrity: sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==} peerDependencies: '@types/react': ^19.0.0 @@ -3343,6 +3405,14 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/eslint-plugin@8.32.1': + resolution: {integrity: sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/parser@8.32.0': resolution: {integrity: sha512-B2MdzyWxCE2+SqiZHAjPphft+/2x2FlO9YBx7eKE1BCb+rqBlQdhtAEhzIEdozHd55DXPmxBdpMygFJjfjjA9A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3350,6 +3420,13 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/parser@8.32.1': + resolution: {integrity: sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/scope-manager@8.29.0': resolution: {integrity: sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3358,6 +3435,10 @@ packages: resolution: {integrity: sha512-jc/4IxGNedXkmG4mx4nJTILb6TMjL66D41vyeaPWvDUmeYQzF3lKtN15WsAeTr65ce4mPxwopPSo1yUUAWw0hQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.32.1': + resolution: {integrity: sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/type-utils@8.32.0': resolution: {integrity: sha512-t2vouuYQKEKSLtJaa5bB4jHeha2HJczQ6E5IXPDPgIty9EqcJxpr1QHQ86YyIPwDwxvUmLfP2YADQ5ZY4qddZg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3365,6 +3446,13 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/type-utils@8.32.1': + resolution: {integrity: sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/types@8.29.0': resolution: {integrity: sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3373,6 +3461,10 @@ packages: resolution: {integrity: sha512-O5Id6tGadAZEMThM6L9HmVf5hQUXNSxLVKeGJYWNhhVseps/0LddMkp7//VDkzwJ69lPL0UmZdcZwggj9akJaA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.32.1': + resolution: {integrity: sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.29.0': resolution: {integrity: sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3385,6 +3477,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/typescript-estree@8.32.1': + resolution: {integrity: sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/utils@8.29.0': resolution: {integrity: sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3399,6 +3497,13 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/utils@8.32.1': + resolution: {integrity: sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/visitor-keys@8.29.0': resolution: {integrity: sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3407,6 +3512,10 @@ packages: resolution: {integrity: sha512-1rYQTCLFFzOI5Nl0c8LUpJT8HxpwVRn9E4CkMsYfuN6ctmQqExjSTzzSk0Tz2apmXy7WU6/6fyaZVVA/thPN+w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.32.1': + resolution: {integrity: sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vercel/analytics@1.5.0': resolution: {integrity: sha512-MYsBzfPki4gthY5HnYN7jgInhAZ7Ac1cYDoRWFomwGHWEX7odTEzbtg9kf/QSo7XEsEAqlQugA6gJ2WS2DEa3g==} peerDependencies: @@ -5158,6 +5267,10 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + ignore@7.0.4: + resolution: {integrity: sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==} + engines: {node: '>= 4'} + image-size@1.2.0: resolution: {integrity: sha512-4S8fwbO6w3GeCVN6OPtA9I5IGKkcDMPcKndtUlpJuCwu7JLjtj7JZpwqLuyY2nrmQT3AWsCJLSKPsc2mPBSl3w==} engines: {node: '>=16.x'} @@ -5170,8 +5283,8 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} - import-in-the-middle@1.13.0: - resolution: {integrity: sha512-YG86SYDtrL/Yu8JgfWb7kjQ0myLeT1whw6fs/ZHFkXFcbk9zJU9lOCsSJHpvaPumU11nN3US7NW6x1YTk+HrUA==} + import-in-the-middle@1.13.2: + resolution: {integrity: sha512-Yjp9X7s2eHSXvZYQ0aye6UvwYPrVB5C2k47fuXjFKnYinAByaDZjh4t9MT2wEga9775n6WaIqyHnQhBxYtX2mg==} import-local@3.2.0: resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} @@ -5653,8 +5766,8 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - knip@5.55.1: - resolution: {integrity: sha512-NYXjgGrXgMdabUKCP2TlBH/e83m9KnLc1VLyWHUtoRrCEJ/C15YtbafrpTvm3td+jE4VdDPgudvXT1IMtCx8lw==} + knip@5.56.0: + resolution: {integrity: sha512-4RNCi41ax0zzl7jloxiUAcomwHIW+tj201jfr7TmHkSvb1/LkChsfXH0JOFFesVHhtSrMw6Dv4N6fmfFd4sJ0Q==} engines: {node: '>=18.18.0'} hasBin: true peerDependencies: @@ -6104,6 +6217,9 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + oxc-resolver@9.0.2: + resolution: {integrity: sha512-w838ygc1p7rF+7+h5vR9A+Y9Fc4imy6C3xPthCMkdFUgFvUWkmABeNB8RBDQ6+afk44Q60/UMMQ+gfDUW99fBA==} + p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -6351,8 +6467,8 @@ packages: resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==} engines: {node: '>=12'} - posthog-js@1.240.6: - resolution: {integrity: sha512-Pz5r/LrMchGf9jCVnTXJrbyMhKriZRGLSZ5qt8c8QrPkmG2JOnFHNWmmBlu+iqmzbY3+oROrhwyP4IgQl2z34w==} + posthog-js@1.242.1: + resolution: {integrity: sha512-j2mzw0eukyuw/Qm3tNZ6pfaXmc7eglWj6ftmvR1Lz9GtMr85ndGNXJvIGO+6PBrQW2o0D1G0k/KV93ehta0hFA==} peerDependencies: '@rrweb/types': 2.0.0-alpha.17 rrweb-snapshot: 2.0.0-alpha.17 @@ -6887,6 +7003,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + send@1.2.0: resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} engines: {node: '>= 18'} @@ -7311,8 +7432,8 @@ packages: ts-easing@0.2.0: resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==} - ts-jest@29.3.2: - resolution: {integrity: sha512-bJJkrWc6PjFVz5g2DGCNUo8z7oFEYaz1xP1NpeDU7KNLMWPpEyV8Chbpkn8xjzgRDpQhnGMyvyldoL7h8JXyug==} + ts-jest@29.3.3: + resolution: {integrity: sha512-y6jLm19SL4GroiBmHwFK4dSHUfDNmOrJbRfp6QmDIlI9p5tT5Q8ItccB4pTIslCIqOZuQnBwpTR0bQ5eUMYwkw==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -7402,6 +7523,10 @@ packages: resolution: {integrity: sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==} engines: {node: '>=16'} + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + type-is@2.0.1: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} @@ -7422,8 +7547,8 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.32.0: - resolution: {integrity: sha512-UMq2kxdXCzinFFPsXc9o2ozIpYCCOiEC46MG3yEh5Vipq6BO27otTtEBZA1fQ66DulEUgE97ucQ/3YY66CPg0A==} + typescript-eslint@8.32.1: + resolution: {integrity: sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -8592,10 +8717,10 @@ snapshots: - '@chromatic-com/playwright' - react - '@clerk/backend@1.32.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@clerk/backend@1.32.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@clerk/shared': 3.8.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@clerk/types': 4.58.0 + '@clerk/shared': 3.9.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@clerk/types': 4.58.1 cookie: 1.0.2 snakecase-keys: 8.0.1 tslib: 2.8.1 @@ -8603,20 +8728,20 @@ snapshots: - react - react-dom - '@clerk/clerk-react@5.31.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@clerk/clerk-react@5.31.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@clerk/shared': 3.8.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@clerk/types': 4.58.0 + '@clerk/shared': 3.9.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@clerk/types': 4.58.1 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) tslib: 2.8.1 - '@clerk/nextjs@6.19.2(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@clerk/nextjs@6.19.3(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@clerk/backend': 1.32.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@clerk/clerk-react': 5.31.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@clerk/shared': 3.8.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@clerk/types': 4.58.0 + '@clerk/backend': 1.32.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@clerk/clerk-react': 5.31.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@clerk/shared': 3.9.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@clerk/types': 4.58.1 next: 15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) @@ -8625,9 +8750,9 @@ snapshots: transitivePeerDependencies: - svix - '@clerk/shared@3.8.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@clerk/shared@3.9.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@clerk/types': 4.58.0 + '@clerk/types': 4.58.1 dequal: 2.0.3 glob-to-regexp: 0.4.1 js-cookie: 3.0.5 @@ -8637,12 +8762,12 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - '@clerk/themes@2.2.43': + '@clerk/themes@2.2.44': dependencies: - '@clerk/types': 4.58.0 + '@clerk/types': 4.58.1 tslib: 2.8.1 - '@clerk/types@4.58.0': + '@clerk/types@4.58.1': dependencies: csstype: 3.1.3 @@ -8677,6 +8802,12 @@ snapshots: '@drizzle-team/brocli@0.10.2': {} + '@emnapi/core@1.4.3': + dependencies: + '@emnapi/wasi-threads': 1.0.2 + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.3.1': dependencies: tslib: 2.8.1 @@ -8687,6 +8818,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/wasi-threads@1.0.2': + dependencies: + tslib: 2.8.1 + optional: true + '@emotion/babel-plugin@11.13.5': dependencies: '@babel/helper-module-imports': 7.25.9 @@ -8951,12 +9087,13 @@ snapshots: '@eslint/core': 0.13.0 levn: 0.4.1 - '@fastify/otel@0.6.0(@opentelemetry/api@1.9.0)': + '@fastify/otel@https://codeload.github.com/getsentry/fastify-otel/tar.gz/ae3088d65e286bdc94ac5d722573537d6a6671bb(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.0.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.200.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.30.0 + minimatch: 9.0.5 transitivePeerDependencies: - supports-color @@ -9173,27 +9310,27 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.17 + '@types/node': 22.15.18 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3))': + '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.17 + '@types/node': 22.15.18 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.15.17)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3)) + jest-config: 29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -9218,7 +9355,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.17 + '@types/node': 22.15.18 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -9236,7 +9373,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.15.17 + '@types/node': 22.15.18 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -9258,7 +9395,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 22.15.17 + '@types/node': 22.15.18 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -9328,7 +9465,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.15.17 + '@types/node': 22.15.18 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -9407,6 +9544,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@napi-rs/wasm-runtime@0.2.9': + dependencies: + '@emnapi/core': 1.4.3 + '@emnapi/runtime': 1.4.0 + '@tybys/wasm-util': 0.9.0 + optional: true + '@neondatabase/serverless@0.9.5': dependencies: '@types/pg': 8.11.6 @@ -9461,10 +9605,6 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} - '@opentelemetry/api-logs@0.200.0': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs@0.57.2': dependencies: '@opentelemetry/api': 1.9.0 @@ -9480,11 +9620,6 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.28.0 - '@opentelemetry/core@2.0.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.30.0 - '@opentelemetry/instrumentation-amqplib@0.46.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -9676,23 +9811,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.200.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.200.0 - '@types/shimmer': 1.2.0 - import-in-the-middle: 1.13.0 - require-in-the-middle: 7.5.1 - shimmer: 1.2.1 - transitivePeerDependencies: - - supports-color - '@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.57.2 '@types/shimmer': 1.2.0 - import-in-the-middle: 1.13.0 + import-in-the-middle: 1.13.2 require-in-the-middle: 7.5.1 semver: 7.7.1 shimmer: 1.2.1 @@ -9723,10 +9847,51 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@oxc-resolver/binding-darwin-arm64@9.0.2': + optional: true + + '@oxc-resolver/binding-darwin-x64@9.0.2': + optional: true + + '@oxc-resolver/binding-freebsd-x64@9.0.2': + optional: true + + '@oxc-resolver/binding-linux-arm-gnueabihf@9.0.2': + optional: true + + '@oxc-resolver/binding-linux-arm64-gnu@9.0.2': + optional: true + + '@oxc-resolver/binding-linux-arm64-musl@9.0.2': + optional: true + + '@oxc-resolver/binding-linux-riscv64-gnu@9.0.2': + optional: true + + '@oxc-resolver/binding-linux-s390x-gnu@9.0.2': + optional: true + + '@oxc-resolver/binding-linux-x64-gnu@9.0.2': + optional: true + + '@oxc-resolver/binding-linux-x64-musl@9.0.2': + optional: true + + '@oxc-resolver/binding-wasm32-wasi@9.0.2': + dependencies: + '@napi-rs/wasm-runtime': 0.2.9 + optional: true + + '@oxc-resolver/binding-win32-arm64-msvc@9.0.2': + optional: true + + '@oxc-resolver/binding-win32-x64-msvc@9.0.2': + optional: true + '@petamoriken/float16@3.9.1': optional: true - '@pmmmwh/react-refresh-webpack-plugin@0.5.16(react-refresh@0.14.2)(type-fest@4.40.0)(webpack-hot-middleware@2.26.1)(webpack@5.98.0(esbuild@0.25.2))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.16(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.98.0(esbuild@0.25.2))': dependencies: ansi-html: 0.0.9 core-js-pure: 3.41.0 @@ -9738,7 +9903,7 @@ snapshots: source-map: 0.7.4 webpack: 5.98.0(esbuild@0.25.2) optionalDependencies: - type-fest: 4.40.0 + type-fest: 4.41.0 webpack-hot-middleware: 2.26.1 '@prisma/instrumentation@6.7.0(@opentelemetry/api@1.9.0)': @@ -9750,19 +9915,19 @@ snapshots: '@radix-ui/primitive@1.1.2': {} - '@radix-ui/react-arrow@1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-arrow@1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-avatar@1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-avatar@1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) @@ -9770,15 +9935,15 @@ snapshots: react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-checkbox@1.3.1(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-checkbox@1.3.1(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.4)(react@19.1.0) @@ -9786,35 +9951,35 @@ snapshots: react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-collapsible@1.1.10(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-collapsible@1.1.10(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-collection@1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-collection@1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.4)(react@19.1.0)': dependencies: @@ -9828,18 +9993,18 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 - '@radix-ui/react-dialog@1.1.13(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-dialog@1.1.13(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) aria-hidden: 1.2.4 @@ -9848,7 +10013,7 @@ snapshots: react-remove-scroll: 2.6.3(@types/react@19.1.4)(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) '@radix-ui/react-direction@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: @@ -9856,33 +10021,33 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 - '@radix-ui/react-dismissable-layer@1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-dismissable-layer@1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-dropdown-menu@2.1.14(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-dropdown-menu@2.1.14(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-menu': 2.1.14(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-menu': 2.1.14(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) '@radix-ui/react-focus-guards@1.1.2(@types/react@19.1.4)(react@19.1.0)': dependencies: @@ -9890,16 +10055,16 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 - '@radix-ui/react-focus-scope@1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-focus-scope@1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) '@radix-ui/react-id@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: @@ -9908,31 +10073,31 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 - '@radix-ui/react-label@2.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-label@2.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-menu@2.1.14(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-menu@2.1.14(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-direction': 1.1.1(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-popper': 1.2.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-popper': 1.2.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) aria-hidden: 1.2.4 @@ -9941,21 +10106,21 @@ snapshots: react-remove-scroll: 2.6.3(@types/react@19.1.4)(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-popover@1.1.13(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-popover@1.1.13(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-popper': 1.2.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-popper': 1.2.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) aria-hidden: 1.2.4 @@ -9964,15 +10129,15 @@ snapshots: react-remove-scroll: 2.6.3(@types/react@19.1.4)(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-popper@1.2.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-popper@1.2.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@floating-ui/react-dom': 2.1.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-arrow': 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-arrow': 1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-rect': 1.1.1(@types/react@19.1.4)(react@19.1.0) @@ -9982,19 +10147,19 @@ snapshots: react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-portal@1.1.8(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-portal@1.1.8(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-presence@1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-presence@1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) @@ -10002,36 +10167,36 @@ snapshots: react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-primitive@2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-primitive@2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-progress@1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-progress@1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-radio-group@1.3.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-radio-group@1.3.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-direction': 1.1.1(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.4)(react@19.1.0) @@ -10039,33 +10204,33 @@ snapshots: react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-roving-focus@1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-roving-focus@1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-direction': 1.1.1(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-separator@1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-separator@1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) '@radix-ui/react-slot@1.2.2(@types/react@19.1.4)(react@19.1.0)': dependencies: @@ -10074,12 +10239,12 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 - '@radix-ui/react-switch@1.2.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-switch@1.2.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.4)(react@19.1.0) @@ -10087,63 +10252,63 @@ snapshots: react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-tabs@1.1.11(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-tabs@1.1.11(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-direction': 1.1.1(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-toast@1.2.13(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-toast@1.2.13(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-visually-hidden': 1.2.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) - '@radix-ui/react-tooltip@1.2.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-tooltip@1.2.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-popper': 1.2.6(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-popper': 1.2.6(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': 1.2.2(@types/react@19.1.4)(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) - '@radix-ui/react-visually-hidden': 1.2.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: @@ -10206,14 +10371,14 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 - '@radix-ui/react-visually-hidden@1.2.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-visually-hidden@1.2.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) '@radix-ui/rect@1.1.1': {} @@ -10304,33 +10469,33 @@ snapshots: '@rushstack/eslint-patch@1.10.5': {} - '@sentry-internal/browser-utils@9.17.0': + '@sentry-internal/browser-utils@9.19.0': dependencies: - '@sentry/core': 9.17.0 + '@sentry/core': 9.19.0 - '@sentry-internal/feedback@9.17.0': + '@sentry-internal/feedback@9.19.0': dependencies: - '@sentry/core': 9.17.0 + '@sentry/core': 9.19.0 - '@sentry-internal/replay-canvas@9.17.0': + '@sentry-internal/replay-canvas@9.19.0': dependencies: - '@sentry-internal/replay': 9.17.0 - '@sentry/core': 9.17.0 + '@sentry-internal/replay': 9.19.0 + '@sentry/core': 9.19.0 - '@sentry-internal/replay@9.17.0': + '@sentry-internal/replay@9.19.0': dependencies: - '@sentry-internal/browser-utils': 9.17.0 - '@sentry/core': 9.17.0 + '@sentry-internal/browser-utils': 9.19.0 + '@sentry/core': 9.19.0 '@sentry/babel-plugin-component-annotate@3.3.1': {} - '@sentry/browser@9.17.0': + '@sentry/browser@9.19.0': dependencies: - '@sentry-internal/browser-utils': 9.17.0 - '@sentry-internal/feedback': 9.17.0 - '@sentry-internal/replay': 9.17.0 - '@sentry-internal/replay-canvas': 9.17.0 - '@sentry/core': 9.17.0 + '@sentry-internal/browser-utils': 9.19.0 + '@sentry-internal/feedback': 9.19.0 + '@sentry-internal/replay': 9.19.0 + '@sentry-internal/replay-canvas': 9.19.0 + '@sentry/core': 9.19.0 '@sentry/bundler-plugin-core@3.3.1': dependencies: @@ -10386,19 +10551,19 @@ snapshots: - encoding - supports-color - '@sentry/core@9.17.0': {} + '@sentry/core@9.19.0': {} - '@sentry/nextjs@9.17.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.98.0(esbuild@0.25.2))': + '@sentry/nextjs@9.19.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.98.0(esbuild@0.25.2))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.30.0 '@rollup/plugin-commonjs': 28.0.1(rollup@4.35.0) - '@sentry-internal/browser-utils': 9.17.0 - '@sentry/core': 9.17.0 - '@sentry/node': 9.17.0 - '@sentry/opentelemetry': 9.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0) - '@sentry/react': 9.17.0(react@19.1.0) - '@sentry/vercel-edge': 9.17.0 + '@sentry-internal/browser-utils': 9.19.0 + '@sentry/core': 9.19.0 + '@sentry/node': 9.19.0 + '@sentry/opentelemetry': 9.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0) + '@sentry/react': 9.19.0(react@19.1.0) + '@sentry/vercel-edge': 9.19.0 '@sentry/webpack-plugin': 3.3.1(webpack@5.98.0(esbuild@0.25.2)) chalk: 3.0.0 next: 15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -10415,9 +10580,9 @@ snapshots: - supports-color - webpack - '@sentry/node@9.17.0': + '@sentry/node@9.19.0': dependencies: - '@fastify/otel': 0.6.0(@opentelemetry/api@1.9.0) + '@fastify/otel': https://codeload.github.com/getsentry/fastify-otel/tar.gz/ae3088d65e286bdc94ac5d722573537d6a6671bb(@opentelemetry/api@1.9.0) '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) @@ -10448,13 +10613,13 @@ snapshots: '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.30.0 '@prisma/instrumentation': 6.7.0(@opentelemetry/api@1.9.0) - '@sentry/core': 9.17.0 - '@sentry/opentelemetry': 9.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0) - import-in-the-middle: 1.13.0 + '@sentry/core': 9.19.0 + '@sentry/opentelemetry': 9.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0) + import-in-the-middle: 1.13.2 transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@9.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)': + '@sentry/opentelemetry@9.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) @@ -10462,19 +10627,19 @@ snapshots: '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.30.0 - '@sentry/core': 9.17.0 + '@sentry/core': 9.19.0 - '@sentry/react@9.17.0(react@19.1.0)': + '@sentry/react@9.19.0(react@19.1.0)': dependencies: - '@sentry/browser': 9.17.0 - '@sentry/core': 9.17.0 + '@sentry/browser': 9.19.0 + '@sentry/core': 9.19.0 hoist-non-react-statics: 3.3.2 react: 19.1.0 - '@sentry/vercel-edge@9.17.0': + '@sentry/vercel-edge@9.19.0': dependencies: '@opentelemetry/api': 1.9.0 - '@sentry/core': 9.17.0 + '@sentry/core': 9.19.0 '@sentry/webpack-plugin@3.3.1(webpack@5.98.0(esbuild@0.25.2))': dependencies: @@ -10681,7 +10846,7 @@ snapshots: dependencies: storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) - '@storybook/nextjs@8.6.12(babel-plugin-macros@3.1.0)(esbuild@0.25.2)(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(type-fest@4.40.0)(typescript@5.8.3)(webpack-hot-middleware@2.26.1)(webpack@5.98.0(esbuild@0.25.2))': + '@storybook/nextjs@8.6.12(babel-plugin-macros@3.1.0)(esbuild@0.25.2)(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(type-fest@4.41.0)(typescript@5.8.3)(webpack-hot-middleware@2.26.1)(webpack@5.98.0(esbuild@0.25.2))': dependencies: '@babel/core': 7.26.9 '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.9) @@ -10696,7 +10861,7 @@ snapshots: '@babel/preset-react': 7.26.3(@babel/core@7.26.9) '@babel/preset-typescript': 7.27.0(@babel/core@7.26.9) '@babel/runtime': 7.26.10 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.16(react-refresh@0.14.2)(type-fest@4.40.0)(webpack-hot-middleware@2.26.1)(webpack@5.98.0(esbuild@0.25.2)) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.16(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.98.0(esbuild@0.25.2)) '@storybook/builder-webpack5': 8.6.12(esbuild@0.25.2)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3) '@storybook/preset-react-webpack': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)))(esbuild@0.25.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3) '@storybook/react': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3) @@ -10960,7 +11125,7 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/react@16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@testing-library/react@16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.26.10 '@testing-library/dom': 10.4.0 @@ -10968,7 +11133,7 @@ snapshots: react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.4 - '@types/react-dom': 19.1.4(@types/react@19.1.4) + '@types/react-dom': 19.1.5(@types/react@19.1.4) '@testing-library/user-event@14.5.2(@testing-library/dom@10.4.0)': dependencies: @@ -10984,6 +11149,11 @@ snapshots: '@tsconfig/node16@1.0.4': {} + '@tybys/wasm-util@0.9.0': + dependencies: + tslib: 2.8.1 + optional: true + '@types/aria-query@5.0.4': {} '@types/babel__core@7.20.5': @@ -11011,7 +11181,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.18 '@types/doctrine@0.0.9': {} @@ -11029,7 +11199,7 @@ snapshots: '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.18 '@types/html-minifier-terser@6.1.0': {} @@ -11052,7 +11222,7 @@ snapshots: '@types/jsdom@20.0.1': dependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.18 '@types/tough-cookie': 4.0.5 parse5: 7.2.1 @@ -11066,9 +11236,9 @@ snapshots: '@types/mysql@2.15.26': dependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.18 - '@types/node@22.15.17': + '@types/node@22.15.18': dependencies: undici-types: 6.21.0 @@ -11080,17 +11250,17 @@ snapshots: '@types/pg@8.11.6': dependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.18 pg-protocol: 1.7.1 pg-types: 4.0.2 '@types/pg@8.6.1': dependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.18 pg-protocol: 1.7.1 pg-types: 2.2.0 - '@types/react-dom@19.1.4(@types/react@19.1.4)': + '@types/react-dom@19.1.5(@types/react@19.1.4)': dependencies: '@types/react': 19.1.4 @@ -11112,7 +11282,7 @@ snapshots: '@types/tedious@4.0.14': dependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.18 '@types/tough-cookie@4.0.5': {} @@ -11141,6 +11311,23 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/eslint-plugin@8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.32.1 + '@typescript-eslint/type-utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.32.1 + eslint: 9.26.0(jiti@2.4.2) + graphemer: 1.4.0 + ignore: 7.0.4 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@typescript-eslint/scope-manager': 8.32.0 @@ -11153,6 +11340,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.32.1 + '@typescript-eslint/types': 8.32.1 + '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.32.1 + debug: 4.4.0 + eslint: 9.26.0(jiti@2.4.2) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@8.29.0': dependencies: '@typescript-eslint/types': 8.29.0 @@ -11163,6 +11362,11 @@ snapshots: '@typescript-eslint/types': 8.32.0 '@typescript-eslint/visitor-keys': 8.32.0 + '@typescript-eslint/scope-manager@8.32.1': + dependencies: + '@typescript-eslint/types': 8.32.1 + '@typescript-eslint/visitor-keys': 8.32.1 + '@typescript-eslint/type-utils@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.8.3) @@ -11174,10 +11378,23 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/type-utils@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + debug: 4.4.0 + eslint: 9.26.0(jiti@2.4.2) + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/types@8.29.0': {} '@typescript-eslint/types@8.32.0': {} + '@typescript-eslint/types@8.32.1': {} + '@typescript-eslint/typescript-estree@8.29.0(typescript@5.8.3)': dependencies: '@typescript-eslint/types': 8.29.0 @@ -11206,6 +11423,20 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.32.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/types': 8.32.1 + '@typescript-eslint/visitor-keys': 8.32.1 + debug: 4.4.0 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.1 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.29.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.26.0(jiti@2.4.2)) @@ -11228,6 +11459,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@2.4.2)) + '@typescript-eslint/scope-manager': 8.32.1 + '@typescript-eslint/types': 8.32.1 + '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) + eslint: 9.26.0(jiti@2.4.2) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@8.29.0': dependencies: '@typescript-eslint/types': 8.29.0 @@ -11238,6 +11480,11 @@ snapshots: '@typescript-eslint/types': 8.32.0 eslint-visitor-keys: 4.2.0 + '@typescript-eslint/visitor-keys@8.32.1': + dependencies: + '@typescript-eslint/types': 8.32.1 + eslint-visitor-keys: 4.2.0 + '@vercel/analytics@1.5.0(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)': optionalDependencies: next: 15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -12050,13 +12297,13 @@ snapshots: safe-buffer: 5.2.1 sha.js: 2.4.11 - create-jest@29.7.0(@types/node@22.15.17)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3)): + create-jest@29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.15.17)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3)) + jest-config: 29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -12559,7 +12806,7 @@ snapshots: eslint: 9.26.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.26.0(jiti@2.4.2)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.26.0(jiti@2.4.2)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.26.0(jiti@2.4.2)) eslint-plugin-react: 7.37.4(eslint@9.26.0(jiti@2.4.2)) eslint-plugin-react-hooks: 5.1.0(eslint@9.26.0(jiti@2.4.2)) @@ -12594,18 +12841,17 @@ snapshots: is-glob: 4.0.3 stable-hash: 0.0.4 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.26.0(jiti@2.4.2)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.26.0(jiti@2.4.2)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.26.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) eslint: 9.26.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.26.0(jiti@2.4.2)) transitivePeerDependencies: - supports-color @@ -12613,7 +12859,7 @@ snapshots: dependencies: eslint: 9.26.0(jiti@2.4.2) - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.26.0(jiti@2.4.2)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -12624,7 +12870,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.26.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.26.0(jiti@2.4.2)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.26.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -12636,7 +12882,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -13257,6 +13503,8 @@ snapshots: ignore@5.3.2: {} + ignore@7.0.4: {} + image-size@1.2.0: dependencies: queue: 6.0.2 @@ -13268,7 +13516,7 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 - import-in-the-middle@1.13.0: + import-in-the-middle@1.13.2: dependencies: acorn: 8.14.0 acorn-import-attributes: 1.9.5(acorn@8.14.0) @@ -13530,7 +13778,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.17 + '@types/node': 22.15.18 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.3(babel-plugin-macros@3.1.0) @@ -13550,16 +13798,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@22.15.17)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3)): + jest-cli@29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.15.17)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3)) + create-jest: 29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.15.17)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3)) + jest-config: 29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -13569,7 +13817,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@22.15.17)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3)): + jest-config@29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)): dependencies: '@babel/core': 7.26.9 '@jest/test-sequencer': 29.7.0 @@ -13594,8 +13842,8 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 22.15.17 - ts-node: 10.9.2(@types/node@22.15.17)(typescript@5.8.3) + '@types/node': 22.15.18 + ts-node: 10.9.2(@types/node@22.15.18)(typescript@5.8.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -13625,7 +13873,7 @@ snapshots: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 22.15.17 + '@types/node': 22.15.18 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3(bufferutil@4.0.9) @@ -13639,7 +13887,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.17 + '@types/node': 22.15.18 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -13649,7 +13897,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 22.15.17 + '@types/node': 22.15.18 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -13688,7 +13936,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.17 + '@types/node': 22.15.18 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -13723,7 +13971,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.17 + '@types/node': 22.15.18 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -13751,7 +13999,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.17 + '@types/node': 22.15.18 chalk: 4.1.2 cjs-module-lexer: 1.4.3 collect-v8-coverage: 1.0.2 @@ -13797,7 +14045,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.17 + '@types/node': 22.15.18 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -13816,7 +14064,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.17 + '@types/node': 22.15.18 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -13825,23 +14073,23 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.18 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.18 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@22.15.17)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3)): + jest@29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.15.17)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3)) + jest-cli: 29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -13973,16 +14221,16 @@ snapshots: kleur@3.0.3: {} - knip@5.55.1(@types/node@22.15.17)(typescript@5.8.3): + knip@5.56.0(@types/node@22.15.18)(typescript@5.8.3): dependencies: '@nodelib/fs.walk': 1.2.8 - '@types/node': 22.15.17 - enhanced-resolve: 5.18.1 + '@types/node': 22.15.18 fast-glob: 3.3.3 formatly: 0.2.3 jiti: 2.4.2 js-yaml: 4.1.0 minimist: 1.2.8 + oxc-resolver: 9.0.2 picocolors: 1.1.1 picomatch: 4.0.2 smol-toml: 1.3.3 @@ -14411,6 +14659,22 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + oxc-resolver@9.0.2: + optionalDependencies: + '@oxc-resolver/binding-darwin-arm64': 9.0.2 + '@oxc-resolver/binding-darwin-x64': 9.0.2 + '@oxc-resolver/binding-freebsd-x64': 9.0.2 + '@oxc-resolver/binding-linux-arm-gnueabihf': 9.0.2 + '@oxc-resolver/binding-linux-arm64-gnu': 9.0.2 + '@oxc-resolver/binding-linux-arm64-musl': 9.0.2 + '@oxc-resolver/binding-linux-riscv64-gnu': 9.0.2 + '@oxc-resolver/binding-linux-s390x-gnu': 9.0.2 + '@oxc-resolver/binding-linux-x64-gnu': 9.0.2 + '@oxc-resolver/binding-linux-x64-musl': 9.0.2 + '@oxc-resolver/binding-wasm32-wasi': 9.0.2 + '@oxc-resolver/binding-win32-arm64-msvc': 9.0.2 + '@oxc-resolver/binding-win32-x64-msvc': 9.0.2 + p-limit@2.3.0: dependencies: p-try: 2.2.0 @@ -14635,7 +14899,7 @@ snapshots: postgres@3.4.5: {} - posthog-js@1.240.6: + posthog-js@1.242.1: dependencies: core-js: 3.41.0 fflate: 0.4.8 @@ -15197,6 +15461,8 @@ snapshots: semver@7.7.1: {} + semver@7.7.2: {} + send@1.2.0: dependencies: debug: 4.4.0 @@ -15550,11 +15816,11 @@ snapshots: strip-json-comments@5.0.1: {} - stripe@18.1.0(@types/node@22.15.17): + stripe@18.1.0(@types/node@22.15.18): dependencies: qs: 6.14.0 optionalDependencies: - '@types/node': 22.15.17 + '@types/node': 22.15.18 style-loader@3.3.4(webpack@5.98.0(esbuild@0.25.2)): dependencies: @@ -15686,18 +15952,18 @@ snapshots: ts-easing@0.2.0: {} - ts-jest@29.3.2(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.9))(esbuild@0.25.2)(jest@29.7.0(@types/node@22.15.17)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3)))(typescript@5.8.3): + ts-jest@29.3.3(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.9))(esbuild@0.25.2)(jest@29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)))(typescript@5.8.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.15.17)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3)) + jest: 29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.7.1 - type-fest: 4.40.0 + semver: 7.7.2 + type-fest: 4.41.0 typescript: 5.8.3 yargs-parser: 21.1.1 optionalDependencies: @@ -15707,14 +15973,14 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.26.9) esbuild: 0.25.2 - ts-node@10.9.2(@types/node@22.15.17)(typescript@5.8.3): + ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.15.17 + '@types/node': 22.15.18 acorn: 8.14.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -15769,6 +16035,8 @@ snapshots: type-fest@4.40.0: {} + type-fest@4.41.0: {} + type-is@2.0.1: dependencies: content-type: 1.0.5 @@ -15808,11 +16076,11 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3): + typescript-eslint@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) eslint: 9.26.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: @@ -15940,9 +16208,9 @@ snapshots: vary@1.1.2: {} - vaul@1.1.2(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + vaul@1.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@radix-ui/react-dialog': 1.1.13(@types/react-dom@19.1.4(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dialog': 1.1.13(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: diff --git a/src/app/p/[locationSlug]/_components/OrderItemsList.tsx b/src/app/p/[locationSlug]/_components/OrderItemsList.tsx new file mode 100644 index 0000000..96becdf --- /dev/null +++ b/src/app/p/[locationSlug]/_components/OrderItemsList.tsx @@ -0,0 +1,23 @@ +import { PublicOrderItem } from '~/app/p/[locationSlug]/_state/cart'; + +export function OrderItemsList(props: { items: PublicOrderItem[] }) { + return ( + <> + {props.items?.map((item, index) => { + return ( +
+
+
1 x {item.menuItem.name}
+
+ {item.menuItem.price} + {/* TODO{currency.symbol} */} +
+
+
+ ); + })} + + ); +} + +// TODO key diff --git a/src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx b/src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx index 9dd387c..f44c863 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx @@ -42,12 +42,12 @@ export function PublicFooterDrawer({ {collapsedContent}
- + Your order -
-
{children}
+
+ {children} Close diff --git a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx index 4828f24..e804894 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx @@ -3,6 +3,7 @@ import { useAtom } from 'jotai'; import { useState } from 'react'; import { placeOrderAction } from '~/app/actions/placeOrderAction'; +import { OrderItemsList } from '~/app/p/[locationSlug]/_components/OrderItemsList'; import { PublicFooterDrawer } from '~/app/p/[locationSlug]/_components/PublicFooterDrawer'; import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; import { Button } from '~/components/ui/button'; @@ -36,49 +37,51 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati try { setIsLoading(true); - const { orderId } = await placeOrderAction(order); - - setOrder((prevOrder) => { - return { - ...prevOrder, - orderId, - items: prevOrder.items.map((item) => { - if (item.status === 'draft') { - return { ...item, status: 'ordered' }; - } - return item; - }), - }; - }); - - toast({ - title: 'Order placed successfully', - description: `Your order number is ${orderId}`, - variant: 'default', - }); + const res = await placeOrderAction(order); + if (res.status === 'success') { + const orderId = res.orderId; + toast({ + title: 'Order placed successfully', + description: `Your order number is ${orderId}`, + variant: 'default', + }); + setOrder((prevOrder) => { + return { + ...prevOrder, + orderId, + items: prevOrder.items.map((item) => { + if (item.status === 'draft') { + return { ...item, status: 'ordered' }; + } + return item; + }), + }; + }); + } else { + toast({ + title: 'Failed to place order', + description: 'Please try again', + variant: 'destructive', + }); + } } catch (error) { console.error('Failed to place order:', error); - toast({ - title: 'Failed to place order', - description: error instanceof Error ? error.message : 'Please try again', - variant: 'destructive', - }); } finally { setIsLoading(false); } }; - const draftItems = order.items.filter((item) => item.status === 'draft').length; - const inPreparationItems = order.items.filter((item) => item.status === 'ordered').length; - const deliveredItems = order.items.filter((item) => item.status === 'delivered').length; + const draftItems = order.items.filter((item) => item.status === 'draft'); + const inPreparationItems = order.items.filter((item) => item.status === 'ordered'); + const deliveredItems = order.items.filter((item) => item.status === 'delivered'); const collapsedContent = (
Your order {order.orderId}
- - {draftItems > 0 && ( + + {draftItems.length > 0 && ( @@ -86,10 +89,10 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati
- +
- +
@@ -97,17 +100,29 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati return ( -
-

This is the content of the drawer. You can put anything here.

-
-

Example Content

-

- This could be settings, additional information, or any other content. -

-
-
-

More Content

-

Add as much content as you need here.

+
+
Your order {order.orderId}
+
+
+
+ + {draftItems.length > 0 && ( + + )} + +
+
+ +
+
+ {/*
+ +
+
+ +
*/}
diff --git a/src/lib/form-state.ts b/src/lib/form-state.ts index 81f7c49..50e3866 100644 --- a/src/lib/form-state.ts +++ b/src/lib/form-state.ts @@ -10,6 +10,7 @@ export type FieldErrors = { }; type SuccessResult = { + orderId: any; status: 'success'; }; diff --git a/src/server/queries/orders.ts b/src/server/queries/orders.ts index 6286e8b..0e9b422 100644 --- a/src/server/queries/orders.ts +++ b/src/server/queries/orders.ts @@ -1,5 +1,6 @@ import { sql } from 'drizzle-orm'; import { z } from 'zod'; +import { OrderItemStatus } from '~/domain/order-items'; import { orderFormSchema } from '~/domain/orders'; import { AppError } from '~/lib/error-utils.server'; import { db } from '~/server/db'; @@ -29,10 +30,11 @@ export async function createOrder(data: z.infer) { if (data.items) { for (let i = 0; i < data.items.length; i++) { const item = data.items[i]; + const status: OrderItemStatus = 'ordered'; await tx.insert(orderItems).values({ orderId: order.id, menuItemId: item!.menuItem.id, - status: 'draft', + status, createdAt: sql`CURRENT_TIMESTAMP`, updatedAt: sql`CURRENT_TIMESTAMP`, }); From 499b21c2a6f04c8778f74b306fe1f9d55fa7d7db Mon Sep 17 00:00:00 2001 From: dopoto Date: Thu, 15 May 2025 12:25:41 +0300 Subject: [PATCH 21/47] css --- .../_components/JotaiProviderWrapper.tsx | 16 +++-- .../_components/OrderItemsList.tsx | 17 +++-- .../_components/PublicFooterPostpaidMode.tsx | 62 +++++++++---------- src/app/p/[locationSlug]/_state/cart.ts | 5 +- src/app/p/[locationSlug]/layout.tsx | 2 +- 5 files changed, 50 insertions(+), 52 deletions(-) diff --git a/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx b/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx index 740b149..d09816e 100644 --- a/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx +++ b/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx @@ -1,33 +1,31 @@ -// app/your-page/JotaiProviderWrapper.tsx 'use client'; import { Provider, useSetAtom } from 'jotai'; import 'jotai-devtools/styles.css'; import { ReactNode, useEffect } from 'react'; import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; +import { CurrencyId } from '~/domain/currencies'; import { LocationId } from '~/domain/locations'; -function Initializer(props: { locationId: LocationId }) { +function Initializer(props: { locationId: LocationId; currencyId: CurrencyId }) { const setOrder = useSetAtom(orderAtom); useEffect(() => { - setOrder({ locationId: props.locationId, items: [] }); + setOrder({ locationId: props.locationId, currencyId: props.currencyId, items: [] }); }, [props.locationId, setOrder]); return null; } -export default function JotaiProviderWrapper({ - children, - locationId, -}: { +export default function JotaiProviderWrapper(props: { children: ReactNode; locationId: LocationId; + currencyId: CurrencyId; }) { return ( - - {children} + + {props.children} ); } diff --git a/src/app/p/[locationSlug]/_components/OrderItemsList.tsx b/src/app/p/[locationSlug]/_components/OrderItemsList.tsx index 96becdf..e0bbb9a 100644 --- a/src/app/p/[locationSlug]/_components/OrderItemsList.tsx +++ b/src/app/p/[locationSlug]/_components/OrderItemsList.tsx @@ -1,18 +1,17 @@ -import { PublicOrderItem } from '~/app/p/[locationSlug]/_state/cart'; +import { useAtom } from 'jotai'; +import { orderAtom, PublicOrderItem } from '~/app/p/[locationSlug]/_state/cart'; +import { CURRENCIES } from '~/domain/currencies'; export function OrderItemsList(props: { items: PublicOrderItem[] }) { + const [order] = useAtom(orderAtom); + const currency = CURRENCIES[order.currencyId]; + return ( <> {props.items?.map((item, index) => { return ( -
-
-
1 x {item.menuItem.name}
-
- {item.menuItem.price} - {/* TODO{currency.symbol} */} -
-
+
+ 1 x {item.menuItem.name}, {item.menuItem.price} {currency?.symbol}
); })} diff --git a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx index e804894..80546bd 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx @@ -75,25 +75,27 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati const inPreparationItems = order.items.filter((item) => item.status === 'ordered'); const deliveredItems = order.items.filter((item) => item.status === 'delivered'); + const draftItemsSummary = ( + + {draftItems.length > 0 && ( + + )} + + ); + const inPreparationItemsSummary = ( + + ); + const deliveredItemsSummary = ; + const collapsedContent = (
Your order {order.orderId}
-
- - {draftItems.length > 0 && ( - - )} - -
-
- -
-
- -
+
{draftItemsSummary}
+
{inPreparationItemsSummary}
+
{deliveredItemsSummary}
); @@ -101,28 +103,26 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati return (
-
Your order {order.orderId}
+
Your order {order.orderId}
-
-
- - {draftItems.length > 0 && ( - - )} - -
+
+
{draftItemsSummary}
- {/*
- +
+
{inPreparationItemsSummary}
+
+ +
+
+
+
{deliveredItemsSummary}
+
+ +
-
- -
*/}
diff --git a/src/app/p/[locationSlug]/_state/cart.ts b/src/app/p/[locationSlug]/_state/cart.ts index e360077..27e2a84 100644 --- a/src/app/p/[locationSlug]/_state/cart.ts +++ b/src/app/p/[locationSlug]/_state/cart.ts @@ -1,15 +1,16 @@ import { atom } from 'jotai'; import { z } from 'zod'; +import { CurrencyId } from '~/domain/currencies'; import { OrderItemStatus } from '~/domain/order-items'; import { orderFormSchema } from '~/domain/orders'; import { type MenuItem } from '../../../../domain/menu-items'; -export type PublicOrder = z.infer; +export type PublicOrder = z.infer & { currencyId: CurrencyId }; export interface PublicOrderItem { menuItem: Pick; status: OrderItemStatus; } -export const orderAtom = atom({ locationId: 0, items: [] }); +export const orderAtom = atom({ locationId: 0, currencyId: 'USD', items: [] }); orderAtom.debugLabel = 'orderAtom'; diff --git a/src/app/p/[locationSlug]/layout.tsx b/src/app/p/[locationSlug]/layout.tsx index e3f79d4..4968746 100644 --- a/src/app/p/[locationSlug]/layout.tsx +++ b/src/app/p/[locationSlug]/layout.tsx @@ -48,7 +48,7 @@ export default async function Layout({ params, children }: { params: Params; chi }); return ( - +
From 00da35cb6e9967e1191788517ef0dcccff2729c2 Mon Sep 17 00:00:00 2001 From: dopoto Date: Thu, 15 May 2025 16:01:41 +0300 Subject: [PATCH 22/47] wip --- .../_components/PublicFooterDrawer.tsx | 14 ++++--------- .../_components/PublicFooterPostpaidMode.tsx | 21 ++++++++++++++----- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx b/src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx index f44c863..883b507 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx @@ -12,21 +12,15 @@ import { } from '~/components/ui/drawer'; import { cn } from '~/lib/utils'; -interface FooterDrawerProps { +export function PublicFooterDrawer(props: { collapsedContent?: React.ReactNode; children?: React.ReactNode; className?: string; triggerClassName?: string; contentClassName?: string; -} +}) { + const { collapsedContent, children, className, triggerClassName, contentClassName } = props; -export function PublicFooterDrawer({ - collapsedContent, - children, - className, - triggerClassName, - contentClassName, -}: FooterDrawerProps) { const [open, setOpen] = useState(false); return ( @@ -34,7 +28,7 @@ export function PublicFooterDrawer({
; const collapsedContent = ( -
-
Your order {order.orderId}
+
+
+ + +
{draftItemsSummary}
{inPreparationItemsSummary}
@@ -102,9 +108,14 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati return ( -
-
Your order {order.orderId}
-
+
+
+ + + + +
+
{draftItemsSummary}
From 7665d91fc1b77b83913c565f342844362fc901e3 Mon Sep 17 00:00:00 2001 From: dopoto Date: Thu, 15 May 2025 16:12:06 +0300 Subject: [PATCH 23/47] wip --- .../_components/PublicFooterPostpaidMode.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx index 1125201..6e0c9f1 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx @@ -15,10 +15,11 @@ import { LocationId } from '~/domain/locations'; import { useToast } from '~/hooks/use-toast'; function OrderSummaryItem(props: { quantity: number; description: string; children?: React.ReactNode }) { + const textColor = props.quantity > 0 ? 'text-black' : 'text-gray-500'; return (
-
{props.quantity}
-
{props.description}
+
{props.quantity}
+
{props.description}
{props.children}
); @@ -82,7 +83,7 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati {draftItems.length > 0 && ( )} From 0f79dbba102d30a062a6c6764cebac7988a08c6b Mon Sep 17 00:00:00 2001 From: dopoto Date: Thu, 15 May 2025 17:20:10 +0300 Subject: [PATCH 24/47] fix ts --- public/images/Arrows bar.gif | Bin 0 -> 8708 bytes public/images/Stampede.gif | Bin 0 -> 16180 bytes src/app/actions/placeOrderAction.ts | 50 +----------------- .../_components/PublicFooterPostpaidMode.tsx | 9 +++- src/lib/form-state.ts | 23 +++++--- 5 files changed, 25 insertions(+), 57 deletions(-) create mode 100644 public/images/Arrows bar.gif create mode 100644 public/images/Stampede.gif diff --git a/public/images/Arrows bar.gif b/public/images/Arrows bar.gif new file mode 100644 index 0000000000000000000000000000000000000000..977f02dd9d1d5407627ada41f3ecccfec3237f5c GIT binary patch literal 8708 zcmdUTXIPUYROujs0%D;_lOkQ3kc1u}KnR514OOWEg7jXb z7Zo=kAOgA(0Tl}e-|yY;`FI^XlYhfl%x_x78ZnRGnw2nY%Yj|^9oR~#Q5 zx4LGftFK#ASCdzeM1yj*Sz7%#`2nHnhs*aY0p1iay zHPu-nXCfO@3y7{mfWz5W0~O-y;N|9;j5g}6H-q1Tm6Wleo`T-@9Z zLj7E>hMHM9g?c)vIzv&K5RDME5HDXZmjDMyh}S)Df3*-K)WO-u(M9cS{#z^qh5Sn; zz!M48`uzj)+oz6?p9@4uS{d#nr+|Q{s7lKzsUqYRr6BUMa>_EYsxoqla9KGuSyeSz zImo{U^vs)|^IbJdUHyOiI$I&3ZUF(lYBDmx!NJnO3erA)t}=3}s;V-w@-p)B@G}j# z{{!y;hY+~8zsNrpbY1+N{M>y5+ z$%Hug%E(E}{!Zy1MN`xNUDeC$Kid8QmM;Gt@Be7n-|B&{i;Sge>Wdrh>p(hT~mQbTzB_&_6hdC_%Dp9shXj;e}IFx zlZ&A)5_)Du+TGn*O;9z736dfDq6aV|6p}}oC3XE zyaWEhI{!CT_m9}$bnx;$^Q`OQ=N{zZtncUJ1^HLnYVLoIMeC3D{*871b1d3_#LAon zBlG)k|G&fjM|8%Z-_!q4_iXVW^1FDSG2ZWt+Ek~%e*XA=^6l%F3M&dwrb5;M}%2&pN__@u;y__){@ z92OHD^)NCbJS_Ac<7 zuUMFynVJ|I85&&H*VEO})} zG5~aZ0#XKx6Q)W?6vm-6Wsqs{01Qz$n@p9FUr5f2i6bW^qf*IeWJzXLU2b(jT`{qy zER710ta-5`9)!nfsu7AHM9rsT-4{nYWhO>iyM}t}2D%r=CSG(_^votu%@6%Wre4mJ z+KT@`1iso6=sw?v{*VlwY29Z#dFm6@9sKF*T9Kt?RB9<(g zhf)@*b0IZ)y;QJ=oOkMFqGIpD8N8R4*ye_!g>M$)9>{Nwl4OiOQd`#BPFkr3+-D^Z z-)YS0v^=>JEr%Wzk_(*F2vW36EhCaVB3@cbTH9Z5Ou3Maxd^T9J~gSc9+?qYZqqDM zUa4w;zibu%_Nyt(bR04M3PG;+RUqg6@cr#8^ zIS6`1r0NhFKlGmHD5LtdG{;E#L0S+;71x z*;?%!EIHgf?%gObwYFu~iwJEJb*2^FqU5Muw4*0p;SQZF?&GxrTo;$?Bbjt}`@2+% zw{e&lSLKYRP$JS8b}AbiN@(r+8M#&DKY%{?vzq*7aynM*)^dJoxpERn<7LZ;H)z1h|ge*_L5ba;@ zLpDU5q*-gV-LTy2c61TQTf+hm%r;9iP7$H@-~-XI2(c^e_z;EeNg-K!KM)mq`9)E+ zaz)sE4uc78lf_IEam$^llD3o*tI@{At9axinwqLT_n{}kY<^rJ3NA0RjZXvXh$lho zQ!RFD`g8BbYM3LagSgQQ6H?2D)IWgCpWOB*`WJsiGH7_nYWJmbncuY^{^WFi(FQ;> zjTm^#w1~jh-3z_26Hu$b1jsi#be_p~5{bNEapbjteDK|18j85gZIk4Vc;neklQM8q z;r8XX)2Z8k6)c)Ddp-ED^^)3AhWia|Qt-S`^7V}$h%cdUs0#}13v|rSmxmoqO(ph) zK|XjrG6~;`9!iVYUCqjk{DC~Mb*t{-K9B1%+p?$epb%_F811-SOxT$Yi~qQ$$scFBqmDQ9!UjVtY}>hBRc7P4GkrSAFJ z3#vOR3jrsh6vuQCW<{x{#*3-~MK}g!(qPV{@Mvp$WgXQT)I+?*NMsM%ebVhIM)~n% zPx3rSr%xN*f6ym73fN7QFR|&p-uObA{e#Htu_L^t%`NO6awWaQ9P$T>qv-`vvD{*q zL=3fhEDD&AgrUEb3X}w7U`cW586eSAuu@J2E;T+Eot#yel$k}&tuLr9E-kMl}I2(}$v+L{C-D&ZR6!Rx|Ca%l?I7 zT4NoZ;~V~z%els}jp6Rd?e@-4k2r{$3UiiO!-U5&@8!<@J>7-dl!SbrVlH{i!i+SF znwF+%V5S#{&oeM}(oLb!!@NG6f*NAQL`v}P93t2}lvKb{GkdP_eVqr-UxN<;<1XkJ z_Gb4(=b>bi`diJ%oP`7Ywk#j=0;c$vOc_(uR!;6$EkDsZ9g>#Z-QAOK7J|XbR@wSY z8!ih6SodeGnIuM>PJ-36{uZN6cm)=eqC64q2@`qS6yNg+MtfP#;+jNn$vZvfC`W>E zYg6%jHCjQVR^mux`h)%V68P}3=R#&+Uu5_pZ7f^0ghp%fk9uG%og?ZYym;kcjIB(; zJaQ%E^NZPs==hI6f&#HC!p<@ezkPqb&-$7p*1Vv^oBCaHV4=lBHt^Sj3FQs^n|s#9T#lo|O|s;?#@&M1%7g)I$` zE}Omo#+A$CK`)U<&82${!CpRRtmDQ;&l>y7UXq}fG6{ZP!eAO|E=&5DS*dZ3lbua< z8C~M8A_kk=K`iO|yHi&s_IV(H9WU`Uga(gl4GJ&&h$QvCkr|wTH!`dn@=o8AsGkAC zRcHC1-IK|NSxLc`Vy~i`6aQGbG1NS$SfKnFt*KzKC`}bW5;i6lES^E6R!+?(V)J9u za+1ziU4%|fEh~DIMSfg@Dr~GxucA+hJSC_Zw?BAzoCJCoTB)%0F)k*8xP`3Z zkvXp!!L)poLT}EEaPh_sXN{KU}NZ9T}ChDq` z_2Cz1FE!+9_|=M+>X z9$aG-sjJ~tP#+>}ZvK#SXezlZSBq;I<$`1oX#hMYI9Y#e@Euu zQ1%)<@a|LoMb}ctyRF%DEfRg=z1><%Tk*!B-GkS?USRGPS?$~GdUe>)O2pFx`qkue zlnfo><5&83kA=EWUp~hiQaKd*A5gUxf~j5xNBQ)bF61~d(gfKX_Uqa3;kvGf=ixek zDi_Ofg~s2|f5udC#S{p+{?{GR&5bK|(p3^g34=uvr7o=x#Q40!5{~2XABq$R)>qzR zc&@j$5$4B5(Ilk}lq%R|!_FY6tn8|RDc?eRFHVf^O05lL*qm~Rpi8`UO_wXn(vfe= z3{9r*2e?&nx?}{jJ)leYVnJ_u#XRMKxa|Y>ws)412Ve;Vz(I$C=AZt|c$VKA$!xLliR9e4?DAqlSw=M~ zzv^*m0h;}3WfI^*N*os(86ffq#n745oyt{@1+e#^pi|gqsiOlEz5N-3!>`CIs22mX zsS9mFkL4E8UBiDDeAr<+nmb%&=a?MB?p=8KuDVYn1P!{@m-Qa288Y*OzO=hYuz`_L z+z_!Zp+Bf6v=zZ9@rhR7`~pId{Yw4XKpI9l2uHSX?o1aSsjRu?&@(|UaIU+j`d-H{ zRV(w7!>JC>eW<+swTqb|OXQYbe&O@e-} z$HE!&?w8J^uRZ0(=c|f;h#(3-h5&+vj=))8E!G^dEb!x_EZK=+F{UBUa0>1&L=tcn zSySRn|BRb6mD}nh&2?D)wktzzyUp$-#q))7Egw3W8pz49Uj_|%2kK8gm52^+IbiH_ zee|(|zy9FDv^ZDriv2Uctr2?H+hvX?2RpI_sY1HnTOaXn^|)eW3PSE3?tJinDAe^G zdXiE*^fDr{?btR<%sTj$Uz_TdP*}$|u2<0|%XjUtV#r}T9KMp%Mlo+-O5m=L;Gk^+ zhTz6yb}2}4-8i!Ts~{e4*uPjBw@DxmwjDyr4Y|ab(r*DUcSWLOyQqxB z6$KMHx_BKo)!luaWV%8}oe4w+(O(i#>Cw1T&05}QS%XIG8^IeaW)r0zpC6-B>X-LB z_oN3py9a5MF!pMon~<*+d%?g?_lGV7gJohs#c|MKlQ?7Jc-fP%=UC!UtcmGpPAG|0 zBnKT+P?S>|PfkxJ5sHZgmH7O)!s16+4|$@yfD$9K3Fq{9_xLLpI)S6E*9xvSY_LqSl#VirQ7Sm{ypRSWn&w3mFfv`g`zq@igvOn7&a{R?-!t%#mB)~i+iz<4;d#*q{S-NeQN2rAEif_@ za3V!0%w@?)}MDg&yrY zmb=BY_F&l!M=7{3a*7|=Tr>x6lk{DvRGWq#+Uu?OkL2;XmPg7$5GSs!OVV&3~ooZ zTFV%m4?oziABeaCuR}krVD8AI9tyX&j9cfbl4&>n{xtB-L@@2~^UhYf4}q$sJh13s z@IfG1{ZunjHEX`GFTDCpJELv)IAw62OxnjkBZm@OQjYb}tXRG!pX$2%7m)4?E%5xT>tzvsKo)3RV&k1H=GXFck*|?ItHKgz zpK*D*8+CLVF3B-j=M|ucy%P> z5Ifk;T^qh3+nh+<&h&+Dz*8>;S}e#-ANm7)CB5&V=tG0^^dR)GE-#ngTsJ#REhpDf ztk*q(dMH7{^=<$RyA~r@6rNL);3#GcF=A_Q7C*c_?PCz;SJpE(^dblD&Fr#3T(!v^ zj9Osu4Ek#^&+&AnrS`~A^mW^s#gnB`dGty)aCe4x_X~8b%k_(NOkDOS1tIJOV<7(V z{aQga1O}1^g{CwLOE2t6xhrVQNZy|tx6f&Gohnf&{^ei?@er!gI-=|OG~i~~+E^Dl z{XN)y4F}l1(@I4=SZ|1ai~bnj3j1q1_4J0Ot;()01plbwP5RTIS2-hpZ!LvqJlJ2y zo`#v{hDR+!=4O3_q`~bkzeJIT2PfDw^wWP-cJ zr5iLy6IQ4k-PFwF4udVb`{neTs6a8lbS>|#_sljgIB}_r+xFe%M0GNU#jRde)k=MQ zCcT=S}<-LR%1FXmF!_?}&IrTM^t zA^cfu10!oIvw&HBYqwt#t&^~4wD`R2_e9Z=Ye^{$G5zaWf4B^4svyxZv1grgB3L3e zg-SUwQ3a3^6Q2&2&mIMGB}H#Vub= z57SJ-7Sp$(fVssW7+JhAcC5W)s=N2BbM9OipPY}Gp4~t%rgM*wK!WYkt1$zgKEIzy zSn z%3>g_$RqR3M5=CPo#VbW+AKy8-U=B!TEE^5IZevBaN}5Nrpa?CRHJD94KS6%QO$9x ziOdl2Q1rqR&)R**&cyGrMl}+$YQ62u=5NNb3*MHKxDg9d_b+eMr+9vaYZPB%9Y67a z2u3V7L7!8&std+nD(&5A7`ki0sPna*rJ9?u>!!x)fe>wuakw|tw~cqx$-9k${Lc=1 z-Zay`)m&0O{5zIcg9rU)3ix?BpF_y=x!TWf2TCn6hk~a8r<)g4Y_EjR2ic*Un0S|w zUACG@ zU4TAkf-MRA)!NY68g*JtTah;?>}u492G_d?Qk6{%*Ujmlkg;L2=j{tOJ9s$smZu)B98ZZK~p zcC7cs=nQ^-u>=V3M}yvBBtGsF_ZFsjX2Bl{_rHJs;r?WiWfrAR#l&nMU`~3j_hWnD z5@DZ>ieDKqLUH+ok>xw|TcUnXm`xyvtbm7y-AYlZtcLuYBNi3%*%-5v(r-FZrZ+F? z<*$ag0n8G0nZS_ZPqXR;ngLrJvAymVMmomYx@nxlGZpHl?FvTo@1K=g!VfqtuJDZI zNt}A7L>NzBq-we!)?$64>~X8me@x9Z>MGjK#|Q3vTiw z!8TKs&!AxAkNv+Up5{l?FE`%~06tfFd87H8|6fkCqL<0Ystf4=`V{&v!!iAAymq+y zWqk)6AG=PD!y)J0m+D1FO?<54&k^V(u$7wu{-W72*-2(y;#{&azfv;GrG-9=b2rll zhNqK2`?_gHm#w+38MCtyVP=u0%j36h=~YhLKrLk_WE5m#_|ujg-p^Rwumx9SQxlfw zufxkLr!z4QGmchmdf#}mf|*7(>|D5;A7uw$IdVJKVmsK+!po>M4w)2VIB1_1V0Hn$sUAI~i^Sq5QSkCIb-=*yNp3`(8b3k;#t`VvF8}@DWn9hj7$>K+4Bb}9g|y8iX!G^=cHB? zV2cPPEO_2>!dc5q^DLg3jG<|cXRBtdCyd}(hSF%-dg40q-FbaW)3KA-@!{&JJWje+ z3@=+AT|bIpK9+eF!?G49^6_}@-I*@Ui^_BQ+PG=uX~Y3cuuytCoMNB*z|_`UYPnJNbWIz0 zCq0cWmt7-L2_{@ZS1*!S(loc|tqnkYSf3xOd{Wev2SmL(*Z+xSI4$>V4w6 z1J8-yI{50e^W=upyYD|Pf8LzR7<H$QSUyQLiro)!+w7F<42Kg7+ybCt1Z z#4N_{aoc&7bCm-g0QK)eqR=n{0sj`h_#zMg`Ayba+_hU-HVN|_IqKQM+Sg5zU3iyc z-5xCQ#?yPvlPt8fh$LCh5gWXzw9&@3WW$M&VJ)-zqa+g_usfHs+NXE}Vv1!?HZ#`5 zatm!`IJi`r=CKL9L)4C(vz8`qZo80VUVb&s=C&p?=SZqnzBOm7?3!eZQ)C8-Z_UbRF23b13{r+{m%cW5>0X{;e^LoA r^o25r(8dQcTndtAy;8L)kx^MwQ3tG+EVo;itg4#fPoz8RnAQIawo4c# literal 0 HcmV?d00001 diff --git a/public/images/Stampede.gif b/public/images/Stampede.gif new file mode 100644 index 0000000000000000000000000000000000000000..9c4a919c70390b5869d7505e09f5ae80b67613ae GIT binary patch literal 16180 zcmb`uc|4T;|Mz{&f*A&5OpGPQ7^1}3#&S|)HftrRXdPQoc9NvbSjRFNOSWVSNt->% zGM4N`B}tMkQ7T&1S!ee#I?wO*d))W+yC2tiU6+4C$1#h~b6%g<>-~OztZkX5W^O3} z26$`=#74)?&CmV#{^Q4AKXi0;Mn*>_#wTLqW3d?Q((9$>rsl5huC9(QosBw6@0KDX zA~UlxKYjURWMDM%a3nM=G&4POZf1_CNi-rGS!}U@i^7!^l^yIHnp>MECMQ09_(W&Y z=~TLfxrL{@CrS#Hl9b|k#B=ceU}<@&!*++dn!37%y8OKS!T!N(mDf<>D6AZ|w4_v7 zMVUw-`X2L*JR1pz!hfxO0spfvwO{%f%{ zJkZVKxK}9N-Rr22za`=Qoi+mA$J3Iq*LWLgTOiHL+s7s%*lSnBc4v=>;~r+7gsoff z7UAaMet~{op>FtaKVSb4^KeUon`eN#mpS;o@Yi|-{Msd<$1Mp~!vDYvpOY34?1eYh zC2#i7H#EhYnCa>po0%FI>EI1W`eZ$lnV!DUW|F=+$;_OjkN^9N06tr==Mi%!I`i-6 z0)Mh3c!!1tn(OJEJb6<0q@iv=@KHT|Gcz+il7XIq!DjG^%^|1!L*2qR`-iCi^9XdW z5RYJ=z)+t6f4uM!-P{9Cgjy27SNi)c_yzvwVf{mN*WQk-j(K)&Fnr z72@C*2tG62E7&K@%aa)#;D=v(+vYz1?Q5}O{I`$y_r0F~?Q5}O{zkd1r>Enl$<@fL2F1>lZxbSNJ z<=pJd^wf*V=M&GypN>5leLV8$;e+9!`}YP1`ulo&y6<*%cC-uHT3ed$G~I5z)lgqo zTT@+i^TzdSS1YenTrMvwy;M?MR9J8^|3Y4FPIlJ$%#8Fj{<&0MN^(*nHz7VQHYS?G zjyiiL@^r+h@RMODLPLUs0t5X0e2*XV@jiOQ%hSW%jdl3Y!2|pExw`D#v)g%>(@w`7 z+qXH`+u7P!GZ}Om)rzupi>1XM=4PfQWMd;k1AUU7?q;1$8@07IXlfAE32Jy%mG#O> ziV8S+xpi1sj0{>@N)ja@jzoxwiojt|P$1O4!I7eXDWD_N6QK+OqQ3+e0&R2@&-IoD z_xYL^4p)TQUEg{{5s+laQQs&EJ07owtZ9VCkQ!#7`cPVGDmx2el}8~@^CU}Oj< zm>C)tZXFSHh8uzsGd31OCDLK*#efV{91Y4(&&;C1P!}@R7f{R7@=K{jH!d-A^75~m z)RdgBzg$w;THC^aq+I~S9$)Am zeA>4_n|z$HLX~*>Sb{0>>C>N#+nyRhw;2&{xE%I8vSzoX`lo6b#dJu4yO9^05W zV;NClF^O(;^lVF1`k6cE(n++j-6W7t z=hpVc2XR|?P|EIYSK@+)lBCSU4ZKaR?UuYqWiCA&JJz1mOOSXoc=L*n-ie%~s?AzW z@MrGStS63|4XtuvA5LElxK8o&kU3VTxuVd;{xav(+_OS}WvOj?=*z9m9V((Ft88nk zTYOZp>*}F<`|Q#GZb{0-;m@nWmwr?+HM3#{{gZcKE=*{etrz>fKo$`ytdplspRtC< zMcaf&M4pX1$7PEr$EGGQ6FD(-Bt9erBg26GUa4Ff^zxOf7ir}{_4@17@`_4SE%jz| z4HI0yhO4(t+N)c;Pz{xhLml_&8x@O#6*n>!WEmD7Mo+tzkUDa)w|b| z8ds)gU%u*H#N02CC>}L@P?SZI`hq_#Lx|=4UFO@-o^j>JR z>>uxHcL#GBnF_QU*B*QjWyc4rk>a3Jmo9jYklB7^lnl#EZ_sLt36jnXR68dexzOO^Lv-<3%9p4eSR}md;G-> zBI4hz<9VjI))Qh07SXg2+QhH9?Wf)Opj|N+uUdb0>t^(1fV>LhEE_Is4OI~rS2=18 z6HO6?*+A2I=a^|3=M!@>vyyXd^2Lh_Q&Jf)bv9eQz(iHyCRt620bRfLG0X!$ctESz z`kGEuOIte=I@mLGt+`v!PknGt&_V6$ZtJ`7@P3DiL4!Z>Hg(f$@+O9y7|^};N&ITO z3SlmM{sXA8VITVX;q9jB0abQ1eZlNU*$}HO3i9ULpCT*qP|UKg4$cX72wdWhIHg3V zvaO;_r|(TY>DLvZYCk=+Y|-_Xxi|?D5UaVb!Jtm5#!u6jFr{qJ8|IPC1PaaMH@$w_&6eWrI$ryw z=EOpF>9$(UPp=+}n9(&qg@tH6zB2lPqIU9nb0%k9&&3`4{QH5=^WE1~-C$Y_jv3k^ zgEXfv5s?)B|6a2La6@sOHza(svJeu7PXer(Nx!wibzYRBXRNZ7(P`_KAX&8Y{-hj z$$|`siwhyRBCc|hSy6fIM%68UmC72|+t>Z8TIuT)3!>0esAex&vsOy2fUpJtD9G#E z>0@Hg#89;~C~9WnW!-wQ@wpeT7H5|jW8)K(b)VkMKf3Q5SmfarQ!A+w-xxl<7?(tV z)Y)1LmviGz8c*#eVki@I2_`giEK5evttM|D3`6d8XC^Uqn__PdvIG)He;q^xr=9;x z@%}NaD}P7HP%*<$mqg7hbt+dd+)`1E)wcE9ooK`ppQ;#*+*vDLwTODfleK7pGBll! zJ6~~4&r9#iVvuU#)*gNu@a+alMq^b{8M{F4031CQhI!XvP{T; z&x6?!jE-6B1@S4$f*LYCFU8gUb>kzdQ^-NTYGd0x>PFI@FmtJ)U?-%-RSH+@geK>)&0@mjZYe@bQuy@r+M$@p(!8eg1m(7*}66=!6ieg z$PL?HMxRZ6vhlB8g`$qA#xdxWWPJroz^O-96mpmj6!wN9v=vuPndsR?P)`AL+k-M< zxd{sJ(rQs(D%}wf*Ut}RB|S~ln#JwIIu%K!hOCC^`6)*i5Y+j{C zX*2Lb@K$l1)671Tz;(LOchsbm%w|f|%l1EkXtZ)nUY|PI+j7Jr&ibpRR@%8ccf?-t z5Mz5|ep>7vLlkwKd*=aZOY5a}MKw4C(n-B+Yd&NX>Jlq_*n&GHE^33DHbg4wb9O;y zsCF!EsMJo1M$&=cSQt8o6uqIij(b-T-e?7gw2DxyQUZo^tRebcq;^$X1W=kx-&4$h zcvxr&m&csMj z>u+TcVpY>yZqr2aiosniO|fxPB2GS(OX@boU~Fqi%d zu`PnVh`zk*>Yi%~@2Sy1&)C60JixJqd*POv>sdCEVa7>1Hs1j?c0+K+!|R;vUTA8> zpHknBn`pz-V6YO5v>v*t_{H|WDgC}yFRv##__uU9LI_}3Apy=Jft_XXWX}&z^ z@CembLRUe$%q{~DM-|=E?Hp7fpGQ78b@iys<CxyU(ZEj`&1sk!>CcAXu*o#*_AXQa&u_u_;ydmW>hT^7B)_CEG4Y2f_ z%GNc(_uki|MgqCfs7aPUY$xlrj4 zTs&bGy&=%l7FLR$Znw)|7bVGDYlIHy*cix&K|;|K&h9&o0?+k3{#p@ahEZ~d;QB?5 zY%f~`%RSvwN{C@jxM-edPW4?JBuo2Me(0#rtBc|DNXEb3E7yc>jl{1N>oTgBO^ufM zcyz3_Ag5?-C4w0m1vfs;I0HY4!6L0?M0B-8WbB~FgLADxC<_YLpe!sZ0ii4}fXmV= zuaS#EG8GH8E7r77M=GzAip13vU^+~?d&z_Z)r-CTREWZTTq;#XzNW4U)7ZDcSGd!H zc&aR@Vrtp-hq!;2z{v`Dere-+%PI0sWYa#7N>1E2mB z!U+IQc9!(6j_!)}n-S^zU;;=b?OO0p#o*2k2_!TV6W-1N;1V*3z^QF`j^$=}x+AGH zzE+VTS2{O#f+N!&yGv2Z!ewJjXUv^Zmq;4hwZUg^^@-C@pT;9X$_?|#LE^Y6CD(Wej7^Y@h^g2Kv(1h;_TA|9n+jH( zi`GhB=^pxlYNe->E$U<95)viyE+jlPZYj<*mfOb(-XZr=pD)gO*^;)i-Lnf=@B4{u zt+RU%%T5DfN(XxIG=Txy9L7VddTS2E-sST7_sAwV$87B>R8WV}^~?}y%hx$Usp8*r zVp1WRlKMj_#@!uui6&Umj5a;LNKNY|YNudd`GKlrw8%u}KswEIY5;-Op$(EkwWv@C z*0TpCvM+eiD+|FF^yaX#`ZTz7Ite1KxtbUtmT*2V7**X3Br1#9E7!|j(p|n^|}Uzi1ua0H9M%TxlXlY7ZTT0 z4oc~RM`S{hYT2X5Ag@nxX(m#WP0wf|wRQ3;?eFSuG1sC(F#{(0O|)NGW_gEdo6H$jJ-RavrKejL?!iv{ zz~;xqtnSfPIG^B&Y*&%J}x@K1voRT=K8S&H5S>Pl_%Bz=E2D#mO5h`)va2 zTB2b0G&3u9LSR`t21qRSBVWfd_iGWe$M%FAXo=pe-yHjt0GWUSRbkvXjI{Yx4JXK@ zuI~~WTP7;1-GBKgBbOf-smD^&brpOyVgd7&FHR0-Zry@y5;sPikXs~W*hY`g?5cK(-)8$LQ#}?&EQoI3C(?YRU!({9V0+?ctktl%I1ZhG{tNizVJ-5_o32~8 z+>z>bLrc30xtYwLmDGP74AG%Fe;dejmI@IUbHPjlaLp8e6Fo!;vQ{UeDORVSI10)y z1di(?p+pO(qd@S?qGtHJ&rjzy7;6aUPuXIMN`2x$gdx_YSg9Z;#K&2jsCY~;9bcXr zzwvcNY1XmVS1RHa|Mx8Af0Xv#!q=6-&+hN<3uw_4v&8>p#&0V$14gX0u zmC?A48<;T@0}ZmV9NY>0?BDl<34 zk(1y|-RSLo6c&v}V2xdx)zaMKlChhjKjDJh@9>}wb%tsERtPtRR!Ic`K;bEhnd=a# z9|iY*!BF^`OTB7T_))AC3cZ}}&Mn*ajKf`q_}oEJ;~O?bbauptDOsR`R;XhV>n`|K zS}yx=<6y^v&SI9&&&0tYDfM@zC_pqH0XLnpPNPJ_W7|tA?_@uWPC>&fc#XMO2XGy-7(ot$#QsF^GZ>Ai<0Y{MbiYq-~PqSQ22EU_gRa_ZNhA)1CzV7YlqB7NwjfhhtV zV%9XX!=GesJDgr~JD8Jhf^{sD?485BB)Lqo0K}^xe3s0k6cQ9JbHp%KB^xAp{})H} ztNim_SNbBt!VHj+XB4TgBm=*jElA2Zu&JmGC|kInzoyVCm`1?Xdo02G<#MB~uB;CU z+?XJf6Ro&-rJ}s@b>+2&*BdMU&lOn+bM$jB9kS3pz@>#apJhigBNO1pF^o8P6o_+@ zbUFf)$ViUjoTJC3+szW`mK&s#1vP`6w+uu@Ozx?npZPSw-y++>Su~>ka zOzBXAqO=^RrqA=m6-S@4@$hJ-q;ddj#HILBH&4^{&|sr(Y0n9KLW+w?G&cs_Lyy!l zDC*?=5*pwtmmTT}2pQ9SxIP8g&Qr_Va!`!WAy%enh|q3&=l!tEO9$_gfFDUZu_?(# zYbOj~7Zt;OO`qZ^K>QoU3QiCv9uONCl>rqw@V4ZQ`D52kXWuB3(qo0;psa&nIcI!} z_kIruy)_wzEw>l_WZrhk7=!j(&3S=H`dAYd3H#6_pAk1wmGW)#@U4Obhs}G9p@^#O zJDBSP=Nli-y57=dh+ll24V1^6Uta=TABVL*k8j!;?RA^KZhOgA*ALwTC*HouK3;fe zHsVhMpCrXC_8WE-0v3S4u>=*0f-L;Vc%=Rv5ie!=Woi~U)XrMA0YnWlSn+qTq3JZp zb+wOZ?j0i!I$c(QZ>^8L?{qIgt<}^jrE8U{E<-ffbqU>#kM&4VKSZ-9TblwLq&j<- zh-xmH(jNZNad<8x$>y7_HVw|wz&nT4XlP|SAYV%EOFGr14aO?SCR>D{eBC}QDV{+e z*6&VYj9n4M1&{0Z$erwXu;C&avN1vBAKS2SiLd*AM{}c}zcky&J1DAxK_GsU%87|% zMkT?G;~89dG)O8>`aA-Y!bpvdO{a6u71E1i(n5a6u^I-*YUAxSR&O^ogRBY~K~}rE z$#+`Xx_kR?b>FA84vta>9`v;+3;q+(GH`>9prt@Ay(OfpjtssBaeYh1<@%cnj$S*| zeCpfObN;KLDcdZKr)#xy)lTEdmp3N(Roy$|%e&b}vwepe6lyMfah95_<^!UVqPoqcnJ>YpOM z7%K(QK;CAYQ|DpHrm&w@x?xWO7Q&PWXfkLV4js87mVbo2uV(hHjK5AV>YB*WA)25; zcA$Pe0s@OQ1pNZACkkIDclfW*8bLL^=aoFpb_!DJadFLFnkQ}IvbBdUq=5|-ek)wHNcA7>NH`U5o?sAed2 zhE%S`5kGM`hcPW_s0LBd{^(}d;b7!WqUTq+!zLX*5T ~_Mg5r8d)njW{B*f zira__-?pJ);v#cdbPqf4odR4P+NuY43a4M8%v?okDMu0Zbro}@g1n2*`CECb<{YV9 zEYA;ny?D8Z`M+6#`hVGlHFVFYzPb#N{tvt{aS2RL3f!2>NP@?L@Sc;-MqpAI{Mh(R zdQw_3{Zd>;BDNs+0_cZ@>5T~j+}gGVaGRh51h~5u1h{{Y3@Yv5{o$6u5n9*73F?C< zLwy6!dY>yRwrMYzAaM^{L4KD%kO`Tp;}1VkAqG>Oqyej~N4_uJ&G`EMIn@Ulu-y5C zG$n8~u{f|Af1|@Z5*As}AggqLqu=7VM&jN8dnk7wpZLJd@7b;SxrdvTO`|ou+-(WdK9#PwKiWjvc_9ywzWMkLMD=i&^i`frP9dd>p(Zu= zRYEj#WW&%|;O^1EGf;A8Vblloh$O3rqK-E;!BUF#a0P+q)y$|^Ql6fCqk4(aSZ1e; zjN%Q`*n}7CoM|pO(}fP{sqgI50DUV*o8}TtIk)wlsi!6d3LoHaaW|p^_FpRcGcJ-` zJvDZI&mo@Koz9+9cRdzNhszeRJK#-K)eQOLI4f)UD6EyWVi4v5Q_-(q!TKVt*(X8e z#Ikl*J`xepc`=ezycWzrg=KEkmi@ow1wfGnN#z|4xBf4%#&eUHvFG5%$qXJm0faSO zIuC(KV`L;GX483@rSx)cRtmN_zo4w>!gcEZ5~+6vKvHMAr%WbiX@6t-_C1-94Pv?i z%Is&Hui0?V(xk=7_p8r~zTThWMSjM9hiUPP^b^(@jjZK0o}&3=Z)0_I4^MeV7M5C5 z(0lGPwPwU+XihRmclRjvLx6h1FA&foA)ts%wIr422oTU>CBtRA%+uoM2~g&)A3;KIs%R%hX3gw15~;9<^9Z()7XDYYxX|FNB(_xb&T|BD~c&} ziU)@hZwR@p|BQb0v!QXtqcPpY@M`3xqb=n!lmqLlMvm`%^gi#bb5#~-el)^*SGseR(M^!bEQuY9Q{1k3&UqJDU%&e4r%zXbEYH4^)` zR6?YadB%K3B0L2IFHH#Exx&oUYi+iWqbPvy6K{w1NV9{1LTLak?{#?-}n>S%!8iikGtlmBEwzsDj`BZp0S>p|ooC+g`tddBt;q#SiDz>1`z4`7LL5MeJiuO)NDUJNx(YH6W~`CEvg5aihgu?k zLglo={q}PLU|o;N*`z^98GJSojpw-g6^UVWPr1~(HC@$CDDXX3w>Xr5!qr=M>>8_t ziiU3cg9Ys7?5XZ>{$pbD$&20?lP6ilt3jvx9*pn$Q@>O>T_fiEh);mlDL-2KtOIhS z%<=2&i(7yA%|ff9(6aN-CX(5A;RH`-KJ3Rzbh`#*?kJCe467@o{IwZdWT~j&JVj|- zprXVU=BVjPdQ%gD&?rgM42d)Hq2V#4*&Liy;zqgUY4XO@RbKL@5}f*(C6r7%T&pOs zL#^7e2#JI{vIn*n;VIA!_J=%6Gi>GPbYK^uumAiu!WGXnYfXhRqBT{)Hfwi*!hDv~ zs(!rk8MvbW$3|OOtDO8%+^rO}gamc6(dx3wfhmQ{1un$jwJT5}xBMFu>N1aKX%XSQ zekR~NN25rMnIj1vT`b`ul!wIwFYag#%+ohu+B+~C7%KA5A5m3tPd>JoKs}d_!a8-tCVnKYW)>B0MFL8g z1C4sq##|~KW26P2rtd{k4s-_EZ~;a5Ho+B&ql<_ddfzx3*fl?(v~KR(rBmvfR=*@I zLK>PEGw?$tfB8>378~Y@uA^D_;v3sLB_Xi=@i;s_)&Z<%1@d#PZ1zMe6X>{S82H|s z`(B)t9LQq1su?3jaSuz%L`C;rZUChd(wYTIr>VnkHqZ{&1N;5DH82Rm6GPT9Tyf7; zngWIgp6E>>_&@!)0)-$p{QPcf9RGXX9I)`?VB-O$@z!LN|IKnsqnbc$4AV@te?+jO%BgAO6zxD8W=qFJ%r5(R9k9NAN1{<b^vSHn$G{#Fhv4bi0vK%E^2%mEN1MpeB`e2KkCXF474a2&h zz7d)U7EA%>SO|Whi?jPZ`X~+d6qH$>{KBP6ymyEd2h%N+j(|sBg$IjkimATj$JpY! zJ(YoQOjnRCLU$tBXc+^@jz1v>;0bdmAFS=!Ph4XN<38Fy9c!52ai68FW|CJ;Y& zSa6oQWhsN~Xv2>9vW$Fia(F}%f|44k2x40m#u7^7@TQ)0fedXUd`<+E+4JO$h$~f_ zSOZFWuw#w3A!QD~@<{>nsMSZsOu!gWu5$Eb2-eYb@CYtD9pFN&=&J(A5m7alw_uyK z_>CPJ29ISc5Z;d0?!PDc!`u_%RB9`ay&|*R&X>{)4QrgHJhg)&F*vD4&X8!1Boyv_ z^k%yBYhs|VfT7f@`5^KtwikL5wEkw7YJMWp_U5Vz==AUOs zdv&pZ96JC&TBlq6U{lk$I8maEo3hmGG**hj>Oi0;ZfzIuT7^2q-E9t`K`gc-+>+(R zA*B{H2Z0A{%f?f@bJj_1a>3)6m597Zq}59XWDeP*aOSl_LgDLc@(ap-@TTsflVgi(PIR#}bB`VUU-Yxa zmUgYdO1uS*fbe79nMBS-$iDIzmyo;qX1olan&RN7D-ubENDEm7D^zh9E<72O+jEFC zFzfS;bLp80iD38#J!-)zh7eO7DQgvNay?lYGz0L4hBZ7}>emFei`?Db+|_m;)Y-m) z2fqpRWkR1le?i}XYrzTVBK<-~pxQY=CgdVteZo|mtbY2OJNNV11|lUs3TN{?kQ9(> zNr3)tW5rleMTLz40xVd9rZXUT9}md>a_HhL-`pdjw$7+779aa^tE5ptm+!X^@%Mg3 znMFN(;~MC04L0>1W^7~twzG+IFYycYE+1<6m!IQz2;E*uAy(P953IyW9R@40_!=*6 zz27fkCDw1CiYBM**CgnTF2^4Kq~SDsn5Tv+fB5D?(Zx|qrPf0>Xn}r)->j7c?)9tXS=eqaA2=DQ~Yt!(12r*z%mwgVl!z3bt)x3C5zpu*J0x@PB?YJdQrM zDj;+#DV}$X*`<2@L&0XEqzuIFQ-$-Ry_V-$O8REImv=j&xAbPj|3a5l7n+Lx_^M_y zsT@S5kxMTnd(}4^#=<&eV%jag9q8Nv7-K#TOR9E_NkD!O@ldLGI~VO-S!DpCoEe*P zu%68yFxMGp^U$)Zy@UFD7mr97Q}c_wV6PGF_YLF&eU6;pTj>N3uv?V@!@lVggh(;V z$*us4M~V~AyhA1u^QMqmVKf)kf<|o9V#Qeqwkm#odY;u{sUJP*ij0r*#uD4SnU#gV zTLtCE{uRa7!b}lZ6a-RpTnjF0V^x%oR_#0TY3IQ$0ku#Q1E;`Z@qf(1T9q{}A(0uA z3O7z-q`>3XjBPFg!$0Z#dlY5OAXEuu7E^VLELjW6Y-3du=xN(Px6skm*52IO*27%W z*I*{}(c>o!sLF^6D7F1n@H@@&cLz<7gu7Lkt8a13WI~o|!}15v*nS~}Hchh*>qpYz zBvZ#rU^8nb7PSxvuxX+bv09$t`Xw=Fv8{m$ZUM3R;$K)`0ZXkc{=CV?=9;}6FFPgD z{_<;PM1b}y8IW>uajsSafGn=ds;l~fBVp6hb=(X))>1(w7I0XD*93$YF!175d%YFH z>)Vf_%K^q9ywFRM$4533Ze7_a$~ZQ5Rlk&BhY|Njj0JuK3k3Jq+;96f(EV1}3*GO% zFXV;W5>p&TOBg4QdGBke&nOK*=|(ojxOZ!>7t<08_%L%piQwI8i>E~QE=PZz8*^SD zBul4AIq%bNdPLYcy=|S-=Ss+RlRIZ6L3sC^2H~}l2=IG$Auz#L=O<}~m#C2k6>$CB zI=?d_vFVY-cEe~Ttupwo=r41jvOCNlcs|5>xkJGc=arau{Nv{@DZr=4Rd3F z0WH#HP{e_7RO-B?U0MPLto%d8W#KcnnqbWUa?Z$!U7qJVNkZgZ=u%)`K$P}@UScYD zUhAM}KVANCY+AfL%eNv;UW8b*aOSvCQn7p5xY|P5-*qd6f6?I@yGAxg;KVr)r>V`L z-+gCdp<8<^unf4Ef_1!MJC{>rgI=q(LZqWO#KsCnT#reMw%+UvJ2`bQ7u9FJIB2Ya_Tg2PLqo4*>uLBc+a^!yO!|D2alo>g-Tb zrDsa)eBTYoeqiFS;)nh}c`V%qh(bV=t60SL_Fqu|phIwn9F@FikqqxW^-18UwsFts z!y?;owxHt;-JHwmg_c{(<6MDHhb3<_CRIfGS?>1*{@lCb^?(FE(S8?z@!JYBXJcBSba^8333Nrl_weRj$hd#eGW(u;vE zhmw_&+{kiZoQp?fZWgnd@_YVX&EzfNZQ?RyIF*)Ee&|N#Vhn6JpppMN=`~u~+w}KWMxbW$J(Q^ub%YGVCkzFo_=n?6pEOw zMjt)HV)H%tt~l8nyVPipCpP5A8r*fsa7ggsdv_s|pHcu5`ZcHfB3GJe_szevGD|kW z8$)PNL>BEFT54iUTy%#|iO7ZmHbGqbdLY_9Iq!DI09q^S5ui(JbQuuYAzrMJ>Y|uy z3zt_g1t$pdtE_imO@%`S*hD1_1RqGuht#=l$bb5r#VH7IC(IY^<*(dT@WZm0xiaTD z-Uj|8y9LlF99t~Q(fwD>_F9;|G8`2b3oMF=iQ2T^3Y>4c8670lSIsn!wVA))S?sLP z)}D?B9Yb_L`VOJ0g04Uqy9(E0S7Es@c9mZKJqmNZqVl&D@V18DzS9Cy+N2W9Y;0=w z5%gGZz!Vg&MX$P>G*w*PNS+Bq;RP9&WFm!mNl2sqh7{FH6HfGhLUquV=RV)r;0eIa zelq{D2^Vn4G8#!4?%=;#&kNR^bJqHL1?LvFQ)mNNnutY!E~y5X zZ&vh3X15?SAg&F5eFW#+FdJU5aeUoWMf-K2Tp7fZ&r=t+2TT}yYJ z>rGUPj30LRdfh%^A`1bX?crN8fC-PX){;FEG~u{2Ur=C{NSwO-P1BrUWjuSJ>tj3T zh0(4pS*~4iJ9pWfuk<&jjR03(8lZ52=RHQRA54UY{vf#~U`!yZN~3|Sni$JnH%nDnXB(vaiv2mVWL%2>-y z8F^&MEO0{?jsyPIMsHtmP#N68uc-K2gNF?~Z`9i~-aMC^SIQ9SxNs57O(l7_vmkEw zG6>*-k>BSM8rDvbJV#YgpFW$P0_WIYK3~6J^8U@_+r{yTRfa0z9@P|Y5S_;Z!xJta z4u+>V@bf!;CYM6Q_ecWl<8&S8YeD)RX@H!q3SdU}>^~pH%+jgpm`ch*pa@z- z_S24EXc@44mBZ6_#E;RIJb8AzqSuhq>EcnV1IOS{ zK^y|6%(!frOw`(tL#*S^!4nF&b#Qw%-Q7_?W_KDh^T_94mNwKD?1-UkMICg(ULfE3 znZ(^>g%OyM{~XJWeiB)5B}lH#>Z{hp-nJtOqEgy6lEr-SbQHOHC9s%IfF4ljP1v@j zCG^9h#5d!py*K_e>ADrY@w;0MQlE|ud`j#?$Or;TEWr7d9{TFI8jiWPe*-j5_}$Vf zTa9d;Ua%pmHHL~-XfS0c1z^!&W*S)$Q;7h3rg4xdE&z#b)SBb>i#Sqn-$lIV6zJqd z;jn2g^WS74@H~-*I5eKh8s2=N49YPuu(~aN%wx)mf+wC6oEkDQcljsYaDT^}#KeCn z^0bC#<>Z0dm8}w#c@aGLaH))OKD*+=efB* zR7>B$=&%V4_4wZUvC00y`_IQ;4P`!he1A+zt&0%*RI`h#8HE0pxQ-6hxhguv7Ewo5 z=)-_1oQwqQIM%|22v{YGDNAv}^)KafWnX5pG^^cY)+usZ9Q8{Ds3;|=vr|*jX$XRZ zuH6BaXFK&*x<&F_W+UHfM+xI}ygCkhv@vd5!YXmKh&iHU{NpjM17Eha0I54tS6w_Z zA~tTuQoHGO=RM zZg;4<@-M~Akd=1M00VDFKVCzi-NPn2+_<_DpDf>HRE2e)@@kb75QeXvlFhAYSQzJd zhD&Ce`DY5;l*KA(iIwfiL=SHB`Hv@7=0A(Oy=THBzkNY2?v*Xd-u1ku&n`|Sag)Ph zKt=TX)-%`kF780t6Q}7iF!#?N2GCL#>C&RtKNnBQMG2=rp36+P`*Q;wDe6g9guZCy zJQ*Uyj9w+oDMp%N>%-7|>ja&Ku`YQLu1sOF(Wv(<&s6hY?u){MXsj|yQ401z1(i7H zZ@-nFhMX}~bW*&qWw)sm3arI|=OWnAvEaxiN5%7O0@#5}wTa@SC#5AalX>UBtQ-*m zKSLLlFD)%+{4Fy@Yuj2aDxY?V&y>$Nx)tzB07yyB+KE!) zF={$m@~T~G&|M9iY(BVI9+!GAKD{B}eQUVnPf^dD#u;8R&RBeUVP`yqa1(v$%pQl3 zo7=lbw{3d(bo%JS^%h$Pbn-%uJ-QlFKR^F=e`An}HR=v=As0={srpH-D!El=(;BdR zeO--)GxqK7QXhm3F$lN*K2>6fpR+-|*7onBwn2<|4i zxp*W4Qg}tUvdD@eKzP9IiSg5CkH_Z4W*Bp?mzbmTZx-kfjiUMK*(Y6JXwZs*QGc$u zu|wSN0W-Tl`*TYGD^_Q?1O7Scrf0zDoeS-D^%syIA@+wC)Uf_4b%~s)o4K zsjs}l=EdVxSw{9<)GcQKK-`wZzs`>_l9vk-xj&)h7GS72`>a}ivoDahPXDoqhh8g5 z@+6|U{{g8jor4S$-8^k%n;3z-ZD><>bC_#)xxCKi#2d5n4GLV|cyFS8F8Siqd(Z5k g2+c9mg>B8IScpu@ro;D}Z-0C>e8~_A1^)Pd0LodyJpcdz literal 0 HcmV?d00001 diff --git a/src/app/actions/placeOrderAction.ts b/src/app/actions/placeOrderAction.ts index fdfd025..1ac6654 100644 --- a/src/app/actions/placeOrderAction.ts +++ b/src/app/actions/placeOrderAction.ts @@ -2,62 +2,16 @@ import * as Sentry from '@sentry/nextjs'; import { headers } from 'next/headers'; -import type { z } from 'node_modules/zod/lib/external'; +import { z } from 'zod'; import { menuFormSchema } from '~/domain/menus'; import { orderFormSchema } from '~/domain/orders'; import { AppError } from '~/lib/error-utils.server'; import { type FormState, processFormErrors } from '~/lib/form-state'; import { createOrder } from '~/server/queries/orders'; -/** - * Generates a unique order number using a timestamp and random characters - */ - -/** - * Places an order by storing it in the database and updating the order items status - */ -// export async function placeOrderAction( -// cartItems: PublicOrderItem[], -// locationId: LocationId, -// ): Promise<{ orderId: OrderId }> { -// try { -// // Filter only draft items for the order -// const draftItems = cartItems.filter((item) => item.status === 'draft'); - -// if (draftItems.length === 0) { -// throw new Error('No draft items to order'); -// } - -// // Generate a unique order number -// // In a real implementation, this would be stored in the database -// const orderId = generateUniqueOrderNumber(); - -// console.log(`Creating order ${orderId} for location ${locationId} with ${draftItems.length} items`); - -// // Here we would typically: -// // 1. Create an order record in the database -// // 2. Create order item records for each draft item -// // 3. Update the status of those items to 'ordered' - -// // Since we don't have direct database access in this example, -// // we're just simulating a successful order creation - -// // In a real implementation, this would be a database transaction - -// // Return the order number so it can be displayed to the user -// return { orderId }; -// } catch (error) { -// console.error('Failed to place order:', error); -// if (error instanceof Error) { -// throw new Error(error.message); -// } -// throw new Error('Failed to place order. Please try again.'); -// } -// } - export const placeOrderAction = async ( data: z.infer, -): Promise> => { +): Promise> => { 'use server'; return await Sentry.withServerActionInstrumentation( 'placeOrderAction', diff --git a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx index 6e0c9f1..4eed459 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx @@ -2,6 +2,7 @@ import { useAtom } from 'jotai'; import { ChevronsDownIcon, ChevronsUpIcon } from 'lucide-react'; +import Image from 'next/image'; import { useState } from 'react'; import { placeOrderAction } from '~/app/actions/placeOrderAction'; import { OrderItemsList } from '~/app/p/[locationSlug]/_components/OrderItemsList'; @@ -43,7 +44,7 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati const res = await placeOrderAction(order); if (res.status === 'success') { - const orderId = res.orderId; + const orderId = res.fields?.orderId; toast({ title: 'Order placed successfully', description: `Your order number is ${orderId}`, @@ -89,7 +90,11 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati ); const inPreparationItemsSummary = ( - + + {inPreparationItems.length > 0 && ( + Hero banner + )} + ); const deliveredItemsSummary = ; diff --git a/src/lib/form-state.ts b/src/lib/form-state.ts index 50e3866..4f4036d 100644 --- a/src/lib/form-state.ts +++ b/src/lib/form-state.ts @@ -9,11 +9,20 @@ export type FieldErrors = { [K in keyof z.infer]?: string[]; }; -type SuccessResult = { - orderId: any; +type SuccessResult = { status: 'success'; + fields?: TReturnShape; }; +/** + * Generic form state type that works with any Zod schema + * TSchema is a Zod schema that defines the form's shape + * TFields is an optional type for the fields returned on success + */ +export type FormState = + | SuccessResult + | ErrorResult; + type ErrorResult = { status: 'error'; fields?: Partial>; @@ -21,11 +30,11 @@ type ErrorResult = { rootError?: string; }; -/** - * Generic form state type that works with any Zod schema - * TSchema is a Zod schema that defines the form's shape - */ -export type FormState = SuccessResult | ErrorResult; +// /** +// * Generic form state type that works with any Zod schema +// * TSchema is a Zod schema that defines the form's shape +// */ +// export type FormState = SuccessResult | ErrorResult; /** * Helper function to process Zod validation errors into field errors From 1ff9644ccddab8cf74aae0f16fc2d1689e7a5ccd Mon Sep 17 00:00:00 2001 From: dopoto Date: Thu, 15 May 2025 17:25:54 +0300 Subject: [PATCH 25/47] oidf --- src/app/actions/placeOrderAction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/actions/placeOrderAction.ts b/src/app/actions/placeOrderAction.ts index 1ac6654..ea3d082 100644 --- a/src/app/actions/placeOrderAction.ts +++ b/src/app/actions/placeOrderAction.ts @@ -37,7 +37,7 @@ export const placeOrderAction = async ( // TODO: revalidatePath(ROUTES.menus(parsedForm.data.locationId)); // TODO revalidate public path - return { status: 'success' as const, orderId: order.id }; + return { status: 'success' as const, fields: { orderId: order.id } }; } catch (error) { if (error instanceof AppError) { return { From d63e11c506ffccad4c6162f9003924b9888582a9 Mon Sep 17 00:00:00 2001 From: dopoto Date: Thu, 15 May 2025 17:36:52 +0300 Subject: [PATCH 26/47] jotai --- .../p/[locationSlug]/_components/JotaiDevTools.tsx | 2 ++ .../_components/JotaiProviderWrapper.tsx | 11 +++++++++++ 2 files changed, 13 insertions(+) create mode 100644 src/app/p/[locationSlug]/_components/JotaiDevTools.tsx diff --git a/src/app/p/[locationSlug]/_components/JotaiDevTools.tsx b/src/app/p/[locationSlug]/_components/JotaiDevTools.tsx new file mode 100644 index 0000000..cd72469 --- /dev/null +++ b/src/app/p/[locationSlug]/_components/JotaiDevTools.tsx @@ -0,0 +1,2 @@ +import 'jotai-devtools/styles.css'; +export { DevTools } from 'jotai-devtools'; diff --git a/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx b/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx index d09816e..6d1f186 100644 --- a/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx +++ b/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx @@ -7,6 +7,16 @@ import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; import { CurrencyId } from '~/domain/currencies'; import { LocationId } from '~/domain/locations'; +import type { DevToolsProps } from 'jotai-devtools'; +import dynamic from 'next/dynamic'; +import type { ComponentType } from 'react'; + +let DevTools: ComponentType | null = null; + +if (process.env.NODE_ENV !== 'production') { + DevTools = dynamic(() => import('./JotaiDevTools').then((mod) => ({ default: mod.DevTools })), { ssr: false }); +} + function Initializer(props: { locationId: LocationId; currencyId: CurrencyId }) { const setOrder = useSetAtom(orderAtom); @@ -25,6 +35,7 @@ export default function JotaiProviderWrapper(props: { return ( + {DevTools && } {props.children} ); From 948d4bbb6538f645d1cf65538bc1111500262de7 Mon Sep 17 00:00:00 2001 From: dopoto Date: Fri, 16 May 2025 18:17:16 +0300 Subject: [PATCH 27/47] wip no ts errors --- .env.example | 9 +- next.config.ts | 2 +- package.json | 7 +- pnpm-lock.yaml | 106 +++++++++++++++++- src/app/actions/placeOrderAction.ts | 18 ++- src/app/api/realtime/notifications.ts | 23 ++++ .../_actions/captureAnalytics.ts | 26 +++++ .../_components/PublicFooterPostpaidMode.tsx | 4 + src/app/p/[locationSlug]/layout.tsx | 20 +--- .../live/_actions/updateOrderItemStatus.ts | 102 +++++++++++++++++ .../live/_components/LiveOrders.tsx | 80 +++++++++++++ .../live/_components/OpenOrdersList.new.tsx | 84 ++++++++++++++ .../live/_components/OpenOrdersList.tsx | 105 +++++++++++++---- .../live/_components/OrderCard.tsx | 65 +++++++++++ src/components/Spinner.tsx | 7 ++ src/components/forms/ReactHookFormField.tsx | 9 +- src/domain/orders.ts | 4 + src/domain/session.ts | 9 ++ src/env.js | 21 +++- src/hooks/use-real-time.ts | 83 ++++++++++++++ src/lib/clerk-theme.ts | 21 ++++ src/lib/pusher.ts | 36 ++++++ src/server/db/schema.ts | 14 ++- src/server/queries/orders.ts | 20 +++- tsconfig.json | 88 ++++++++------- 25 files changed, 869 insertions(+), 94 deletions(-) create mode 100644 src/app/api/realtime/notifications.ts create mode 100644 src/app/p/[locationSlug]/_actions/captureAnalytics.ts create mode 100644 src/app/u/[locationId]/live/_actions/updateOrderItemStatus.ts create mode 100644 src/app/u/[locationId]/live/_components/LiveOrders.tsx create mode 100644 src/app/u/[locationId]/live/_components/OpenOrdersList.new.tsx create mode 100644 src/app/u/[locationId]/live/_components/OrderCard.tsx create mode 100644 src/components/Spinner.tsx create mode 100644 src/domain/session.ts create mode 100644 src/hooks/use-real-time.ts create mode 100644 src/lib/clerk-theme.ts create mode 100644 src/lib/pusher.ts diff --git a/.env.example b/.env.example index 71f101f..d12b032 100644 --- a/.env.example +++ b/.env.example @@ -46,4 +46,11 @@ POSTHOG_PROJECT_ID=12345... POSTHOG_ANALYTICS_QUERIES_API_KEY=phx_... # See https://vercel.com/docs/edge-config/get-started -EDGE_CONFIG="https://edge-config.vercel.com/... \ No newline at end of file +EDGE_CONFIG="https://edge-config.vercel.com/... + +PUSHER_APP_ID=your_app_id +PUSHER_APP_KEY=your_app_key +PUSHER_APP_SECRET=your_app_secret +PUSHER_CLUSTER=your_cluster +NEXT_PUBLIC_PUSHER_APP_KEY=your_app_key +NEXT_PUBLIC_PUSHER_CLUSTER=your_cluster \ No newline at end of file diff --git a/next.config.ts b/next.config.ts index 22aff1e..8920751 100644 --- a/next.config.ts +++ b/next.config.ts @@ -16,7 +16,7 @@ const cspHeader = ` frame-src https://checkout.stripe.com https://*.js.stripe.com https://js.stripe.com https://hooks.stripe.com https://challenges.cloudflare.com; object-src ${process.env.NODE_ENV !== 'development' ? "'self' data:;" : "'none';"} worker-src 'self' blob: ${env.NEXT_PUBLIC_CLERK_SUBDOMAIN}; - connect-src 'self' https://checkout.stripe.com https://api.stripe.com https://maps.googleapis.com ${env.NEXT_PUBLIC_CLERK_SUBDOMAIN} https://clerk-telemetry.com/v1/event https://*.sentry.io https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com; + connect-src 'self' https://checkout.stripe.com https://api.stripe.com https://maps.googleapis.com ${env.NEXT_PUBLIC_CLERK_SUBDOMAIN} https://clerk-telemetry.com/v1/event https://*.sentry.io https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com wss://ws-eu.pusher.com/app/${env.NEXT_PUBLIC_PUSHER_APP_KEY} https://sockjs-eu.pusher.com/pusher/app/${env.NEXT_PUBLIC_PUSHER_APP_KEY}; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; diff --git a/package.json b/package.json index 096deb7..28a239c 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,8 @@ "@stripe/react-stripe-js": "^3.7.0", "@stripe/stripe-js": "^7.3.0", "@t3-oss/env-nextjs": "^0.13.4", + "@tanstack/react-query": "^5.76.1", + "@tanstack/react-query-devtools": "^5.76.1", "@vercel/analytics": "^1.5.0", "@vercel/edge-config": "^1.4.0", "@vercel/postgres": "^0.10.0", @@ -66,9 +68,12 @@ "jotai": "^2.12.4", "lucide-react": "^0.510.0", "next": "15.3.2", + "next-themes": "^0.4.6", "postgres": "^3.4.5", "posthog-js": "^1.242.1", "posthog-node": "^4.17.1", + "pusher": "^5.2.0", + "pusher-js": "^8.4.0", "react": "^19.1.0", "react-confetti": "6.4.0", "react-dom": "^19.1.0", @@ -128,7 +133,7 @@ "ct3aMetadata": { "initVersion": "7.38.1" }, - "packageManager": "pnpm@10.10.0", + "packageManager": "pnpm@10.11.0", "pnpm": { "ignoredBuiltDependencies": [ "@sentry/cli" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc2a12a..bf22868 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,6 +89,12 @@ importers: '@t3-oss/env-nextjs': specifier: ^0.13.4 version: 0.13.4(arktype@2.1.20)(typescript@5.8.3)(zod@3.24.4) + '@tanstack/react-query': + specifier: ^5.76.1 + version: 5.76.1(react@19.1.0) + '@tanstack/react-query-devtools': + specifier: ^5.76.1 + version: 5.76.1(@tanstack/react-query@5.76.1(react@19.1.0))(react@19.1.0) '@vercel/analytics': specifier: ^1.5.0 version: 1.5.0(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) @@ -125,6 +131,9 @@ importers: next: specifier: 15.3.2 version: 15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next-themes: + specifier: ^0.4.6 + version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) postgres: specifier: ^3.4.5 version: 3.4.5 @@ -134,6 +143,12 @@ importers: posthog-node: specifier: ^4.17.1 version: 4.17.1 + pusher: + specifier: ^5.2.0 + version: 5.2.0 + pusher-js: + specifier: ^8.4.0 + version: 8.4.0 react: specifier: ^19.1.0 version: 19.1.0 @@ -3218,6 +3233,23 @@ packages: '@tailwindcss/postcss@4.1.6': resolution: {integrity: sha512-ELq+gDMBuRXPJlpE3PEen+1MhnHAQQrh2zF0dI1NXOlEWfr2qWf2CQdr5jl9yANv8RErQaQ2l6nIFO9OSCVq/g==} + '@tanstack/query-core@5.76.0': + resolution: {integrity: sha512-FN375hb8ctzfNAlex5gHI6+WDXTNpe0nbxp/d2YJtnP+IBM6OUm7zcaoCW6T63BawGOYZBbKC0iPvr41TteNVg==} + + '@tanstack/query-devtools@5.76.0': + resolution: {integrity: sha512-1p92nqOBPYVqVDU0Ua5nzHenC6EGZNrLnB2OZphYw8CNA1exuvI97FVgIKON7Uug3uQqvH/QY8suUKpQo8qHNQ==} + + '@tanstack/react-query-devtools@5.76.1': + resolution: {integrity: sha512-LFVWgk/VtXPkerNLfYIeuGHh0Aim/k9PFGA+JxLdRaUiroQ4j4eoEqBrUpQ1Pd/KXoG4AB9vVE/M6PUQ9vwxBQ==} + peerDependencies: + '@tanstack/react-query': ^5.76.1 + react: ^18 || ^19 + + '@tanstack/react-query@5.76.1': + resolution: {integrity: sha512-YxdLZVGN4QkT5YT1HKZQWiIlcgauIXEIsMOTSjvyD5wLYK8YVvKZUPAysMqossFJJfDpJW3pFn7WNZuPOqq+fw==} + peerDependencies: + react: ^18 || ^19 + '@testing-library/dom@10.4.0': resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} engines: {node: '>=18'} @@ -3342,6 +3374,9 @@ packages: '@types/mysql@2.15.26': resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} + '@types/node-fetch@2.6.12': + resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} + '@types/node@22.15.18': resolution: {integrity: sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==} @@ -5335,6 +5370,10 @@ packages: resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} + is-base64@1.1.0: + resolution: {integrity: sha512-Nlhg7Z2dVC4/PTvIFkgVVNvPHSO2eR/Yd0XzhGiXCXEvWnptXlXa/clQ8aePPiMuxEGcWfzWbGw2Fe3d+Y3v1g==} + hasBin: true + is-bigint@1.1.0: resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} engines: {node: '>= 0.4'} @@ -6083,6 +6122,12 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + next-themes@0.4.6: + resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} + peerDependencies: + react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + next@15.3.2: resolution: {integrity: sha512-CA3BatMyHkxZ48sgOCLdVHjFU36N7TF1HhqAHLFOkV6buwZnvMI84Cug8xD56B9mCuKrqXnLn94417GrZ/jjCQ==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} @@ -6611,6 +6656,13 @@ packages: pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + pusher-js@8.4.0: + resolution: {integrity: sha512-wp3HqIIUc1GRyu1XrP6m2dgyE9MoCsXVsWNlohj0rjSkLf+a0jLvEyVubdg58oMk7bhjBWnFClgp8jfAa6Ak4Q==} + + pusher@5.2.0: + resolution: {integrity: sha512-F6LNiZyJsIkoHLz+YurjKZ1HH8V1/cMggn4k97kihjP3uTvm0P4mZzSFeHOWIy+PlJ2VInJBhUFJBYLsFR5cjg==} + engines: {node: '>= 4.0.0'} + qr.js@0.0.0: resolution: {integrity: sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==} @@ -7499,6 +7551,12 @@ packages: tween-functions@1.2.0: resolution: {integrity: sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==} + tweetnacl-util@0.15.1: + resolution: {integrity: sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==} + + tweetnacl@1.0.3: + resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -11094,6 +11152,21 @@ snapshots: postcss: 8.5.3 tailwindcss: 4.1.6 + '@tanstack/query-core@5.76.0': {} + + '@tanstack/query-devtools@5.76.0': {} + + '@tanstack/react-query-devtools@5.76.1(@tanstack/react-query@5.76.1(react@19.1.0))(react@19.1.0)': + dependencies: + '@tanstack/query-devtools': 5.76.0 + '@tanstack/react-query': 5.76.1(react@19.1.0) + react: 19.1.0 + + '@tanstack/react-query@5.76.1(react@19.1.0)': + dependencies: + '@tanstack/query-core': 5.76.0 + react: 19.1.0 + '@testing-library/dom@10.4.0': dependencies: '@babel/code-frame': 7.26.2 @@ -11238,6 +11311,11 @@ snapshots: dependencies: '@types/node': 22.15.18 + '@types/node-fetch@2.6.12': + dependencies: + '@types/node': 22.15.18 + form-data: 4.0.2 + '@types/node@22.15.18': dependencies: undici-types: 6.21.0 @@ -13295,7 +13373,7 @@ snapshots: '@petamoriken/float16': 3.9.1 debug: 4.4.0 env-paths: 3.0.0 - semver: 7.7.1 + semver: 7.7.2 shell-quote: 1.8.2 which: 4.0.0 transitivePeerDependencies: @@ -13574,6 +13652,8 @@ snapshots: has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 + is-base64@1.1.0: {} + is-bigint@1.1.0: dependencies: has-bigints: 1.1.0 @@ -14490,6 +14570,11 @@ snapshots: neo-async@2.6.2: {} + next-themes@0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@next/env': 15.3.2 @@ -14989,6 +15074,21 @@ snapshots: pure-rand@6.1.0: {} + pusher-js@8.4.0: + dependencies: + tweetnacl: 1.0.3 + + pusher@5.2.0: + dependencies: + '@types/node-fetch': 2.6.12 + abort-controller: 3.0.0 + is-base64: 1.1.0 + node-fetch: 2.7.0 + tweetnacl: 1.0.3 + tweetnacl-util: 0.15.1 + transitivePeerDependencies: + - encoding + qr.js@0.0.0: {} qs@6.14.0: @@ -16021,6 +16121,10 @@ snapshots: tween-functions@1.2.0: {} + tweetnacl-util@0.15.1: {} + + tweetnacl@1.0.3: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 diff --git a/src/app/actions/placeOrderAction.ts b/src/app/actions/placeOrderAction.ts index ea3d082..d72f86b 100644 --- a/src/app/actions/placeOrderAction.ts +++ b/src/app/actions/placeOrderAction.ts @@ -3,8 +3,9 @@ import * as Sentry from '@sentry/nextjs'; import { headers } from 'next/headers'; import { z } from 'zod'; +import { notifyOrderCreated } from '~/app/api/realtime/notifications'; import { menuFormSchema } from '~/domain/menus'; -import { orderFormSchema } from '~/domain/orders'; +import { orderFormSchema, type OrderWithItems } from '~/domain/orders'; import { AppError } from '~/lib/error-utils.server'; import { type FormState, processFormErrors } from '~/lib/form-state'; import { createOrder } from '~/server/queries/orders'; @@ -35,9 +36,18 @@ export const placeOrderAction = async ( } const order = await createOrder(parsedForm.data); - // TODO: revalidatePath(ROUTES.menus(parsedForm.data.locationId)); - // TODO revalidate public path - return { status: 'success' as const, fields: { orderId: order.id } }; + // Send real-time notification + const orderWithItems: OrderWithItems = { + ...order, + orderId: order.id.toString(), + items: parsedForm.data.items, + }; + await notifyOrderCreated(parsedForm.data.locationId, orderWithItems); + + return { + status: 'success' as const, + fields: { orderId: order.id.toString() }, + }; } catch (error) { if (error instanceof AppError) { return { diff --git a/src/app/api/realtime/notifications.ts b/src/app/api/realtime/notifications.ts new file mode 100644 index 0000000..631f508 --- /dev/null +++ b/src/app/api/realtime/notifications.ts @@ -0,0 +1,23 @@ +import { type LocationId } from '~/domain/locations'; +import { type OrderWithItems } from '~/domain/orders'; +import { CHANNELS, EVENTS, getPusherServer } from '~/lib/pusher'; + +export async function notifyOrderCreated(locationId: LocationId, order: OrderWithItems) { + const pusher = getPusherServer(); + // Notify all clients listening to this location about the new order + await pusher.trigger(CHANNELS.location(locationId), EVENTS.ORDER_CREATED, order); +} + +export async function notifyOrderUpdated(locationId: LocationId, order: OrderWithItems) { + const pusher = getPusherServer(); + // Notify all clients listening to this location about the order update + await pusher.trigger(CHANNELS.location(locationId), EVENTS.ORDER_UPDATED, order); + + // Also notify clients specifically listening to this order + await pusher.trigger(CHANNELS.order(order.id.toString()), EVENTS.ORDER_UPDATED, order); +} + +export async function notifyOrderItemUpdated(locationId: LocationId, order: OrderWithItems) { + const pusher = getPusherServer(); + await pusher.trigger(CHANNELS.order(order.id.toString()), EVENTS.ORDER_ITEM_UPDATED, order); +} diff --git a/src/app/p/[locationSlug]/_actions/captureAnalytics.ts b/src/app/p/[locationSlug]/_actions/captureAnalytics.ts new file mode 100644 index 0000000..3547284 --- /dev/null +++ b/src/app/p/[locationSlug]/_actions/captureAnalytics.ts @@ -0,0 +1,26 @@ +'use server'; + +import { PostHog } from 'posthog-node'; +import type { LocationSlug } from '~/domain/locations'; + +import { env } from '~/env'; +import { OrganizationId } from '~/lib/organization'; + +const posthog = new PostHog(env.NEXT_PUBLIC_POSTHOG_KEY!, { + host: env.NEXT_PUBLIC_POSTHOG_HOST, +}); + +export async function capturePublicLocationVisit( + machineId: string | undefined, + orgId: OrganizationId, + locationSlug: LocationSlug, +) { + await posthog.capture({ + distinctId: machineId ?? '-- missing machine id --', + event: 'publicLocationVisit', + properties: { + orgId, + locationSlug, + }, + }); +} diff --git a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx index 4eed459..bc6c22d 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx @@ -13,6 +13,7 @@ import { Button } from '~/components/ui/button'; import { DrawerClose } from '~/components/ui/drawer'; import { type CurrencyId } from '~/domain/currencies'; import { LocationId } from '~/domain/locations'; +import { useRealTimeOrderUpdates } from '~/hooks/use-real-time'; import { useToast } from '~/hooks/use-toast'; function OrderSummaryItem(props: { quantity: number; description: string; children?: React.ReactNode }) { @@ -31,6 +32,9 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati const [isLoading, setIsLoading] = useState(false); const { toast } = useToast(); + // Add real-time updates + useRealTimeOrderUpdates(order.orderId, props.locationId); + const totalAmount = order.items.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0'), 0); const processOrder = async (e?: React.MouseEvent) => { diff --git a/src/app/p/[locationSlug]/layout.tsx b/src/app/p/[locationSlug]/layout.tsx index 4968746..541ce88 100644 --- a/src/app/p/[locationSlug]/layout.tsx +++ b/src/app/p/[locationSlug]/layout.tsx @@ -1,20 +1,14 @@ import { cookies } from 'next/headers'; import Image from 'next/image'; -import { PostHog } from 'posthog-node'; import JotaiProviderWrapper from '~/app/p/[locationSlug]/_components/JotaiProviderWrapper'; import { PublicFooterPostpaidMode } from '~/app/p/[locationSlug]/_components/PublicFooterPostpaidMode'; import { PublicFooterPrepaidMode } from '~/app/p/[locationSlug]/_components/PublicFooterPrepaidMode'; import { AnalyticsEventSender } from '~/components/AnalyticsEventSender'; -import type { AnalyticsEventId } from '~/domain/analytics'; import { CookieKey } from '~/domain/cookies'; import { locationSlugSchema } from '~/domain/locations'; -import { env } from '~/env'; import { AppError } from '~/lib/error-utils.server'; import { getLocationPublicDataBySlug } from '~/server/queries/locations'; - -const posthog = new PostHog(env.NEXT_PUBLIC_POSTHOG_KEY!, { - host: env.NEXT_PUBLIC_POSTHOG_HOST, -}); +import { capturePublicLocationVisit } from './_actions/captureAnalytics'; //TODO Use cache @@ -32,20 +26,10 @@ export default async function Layout({ params, children }: { params: Params; chi const parsedLocationSlug = locationSlugValidationResult.data; const location = await getLocationPublicDataBySlug(parsedLocationSlug); - - const event: AnalyticsEventId = 'publicLocationVisit'; - const cookieStore = cookies(); const machineId = (await cookieStore).get(CookieKey.MachineId)?.value; - posthog.capture({ - distinctId: machineId ?? '-- missing machine id --', - event, - properties: { - orgId: location.orgId, - locationSlug: parsedLocationSlug, - }, - }); + await capturePublicLocationVisit(machineId, location.orgId, parsedLocationSlug); return ( diff --git a/src/app/u/[locationId]/live/_actions/updateOrderItemStatus.ts b/src/app/u/[locationId]/live/_actions/updateOrderItemStatus.ts new file mode 100644 index 0000000..a131df9 --- /dev/null +++ b/src/app/u/[locationId]/live/_actions/updateOrderItemStatus.ts @@ -0,0 +1,102 @@ +'use server'; + +import { auth } from '@clerk/nextjs/server'; +import { and, eq, type InferSelectModel } from 'drizzle-orm'; +import { revalidatePath } from 'next/cache'; +import { notifyOrderUpdated } from '~/app/api/realtime/notifications'; +import { type LocationId } from '~/domain/locations'; +import { type OrderItemStatus } from '~/domain/order-items'; +import { type OrderId } from '~/domain/orders'; +import { AppError } from '~/lib/error-utils.server'; +import { db } from '~/server/db'; +import { menuItems, orderItems, orders } from '~/server/db/schema'; +import { getLocationForCurrentUserOrThrow } from '~/server/queries/locations'; + +type OrderItem = InferSelectModel & { + menuItem: InferSelectModel; +}; + +type Order = InferSelectModel & { + orderItems: OrderItem[]; +}; + +export async function updateOrderItemStatus( + locationId: LocationId, + orderId: OrderId, + menuItemId: number, + status: OrderItemStatus, +) { + const { userId } = await auth(); + if (!userId) { + throw new AppError({ internalMessage: 'Unauthorized' }); + } + + // Parse orderId to number since it might come as string from the frontend + const orderIdNum = parseInt(orderId.toString(), 10); + if (isNaN(orderIdNum)) { + throw new AppError({ internalMessage: 'Invalid order ID' }); + } + + // Verify that the location belongs to the current user's organization + await getLocationForCurrentUserOrThrow(locationId); + + // Update the order item status + const [updatedItem] = await db + .update(orderItems) + .set({ + status, + updatedAt: new Date(), + }) + .where(and(eq(orderItems.orderId, orderIdNum), eq(orderItems.menuItemId, menuItemId))) + .returning(); + + if (!updatedItem) { + throw new AppError({ internalMessage: 'Could not update order item' }); + } + + // Get the full updated order with items and menu items + const foundOrders = (await db.query.orders.findMany({ + where: (orders, { eq }) => eq(orders.id, orderIdNum), + with: { + orderItems: { + columns: { + id: true, + status: true, + orderId: true, + menuItemId: true, + }, + with: { + menuItem: { + columns: { + id: true, + name: true, + price: true, + }, + }, + }, + }, + }, + })) as Order[]; + + const order = foundOrders[0]; + if (!order || !order.orderItems) { + throw new AppError({ internalMessage: 'Could not find order' }); + } + + // Send real-time notification + await notifyOrderUpdated(locationId, { + ...order, + orderId: order.id.toString(), + items: order.orderItems.map((item: OrderItem) => ({ + menuItem: { + id: item.menuItem.id, + name: item.menuItem.name ?? '', + price: item.menuItem.price, + }, + status: item.status as OrderItemStatus, + })), + }); + + revalidatePath(`/u/${locationId}/live`); + return order; +} diff --git a/src/app/u/[locationId]/live/_components/LiveOrders.tsx b/src/app/u/[locationId]/live/_components/LiveOrders.tsx new file mode 100644 index 0000000..faa1103 --- /dev/null +++ b/src/app/u/[locationId]/live/_components/LiveOrders.tsx @@ -0,0 +1,80 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { DashboardCard } from '~/app/u/[locationId]/_components/DashboardCard'; +import { type LocationId } from '~/domain/locations'; +import { type OrderWithItems } from '~/domain/orders'; +import { useToast } from '~/hooks/use-toast'; +import { CHANNELS, EVENTS, pusherClient } from '~/lib/pusher'; +import { OrderCard } from './OrderCard'; + +export function LiveOrders({ + locationId, + initialOrders = [], +}: { + locationId: LocationId; + initialOrders: OrderWithItems[]; +}) { + const [orders, setOrders] = useState(initialOrders); + const { toast } = useToast(); + + // Subscribe to location-wide order updates + useEffect(() => { + const locationChannel = pusherClient.subscribe(CHANNELS.location(locationId)); + + // Handle new orders + locationChannel.bind(EVENTS.ORDER_CREATED, (data: OrderWithItems) => { + setOrders((current) => [...current, data]); + toast({ + title: 'New Order Received', + description: `Order #${data.orderId} has been created`, + }); + }); + + // Handle updates to any order + locationChannel.bind(EVENTS.ORDER_UPDATED, (data: OrderWithItems) => { + setOrders((current) => current.map((order) => (order.orderId === data.orderId ? data : order))); + }); + + return () => { + try { + locationChannel.unsubscribe(); + } catch (error) { + console.error('Error unsubscribing from Pusher channel:', error); + } + }; + }, [locationId, toast]); + + const orderedCount = orders.filter((o) => o.items.some((i) => i.status === 'ordered')).length; + const preparingCount = orderedCount; // For now they're the same + const deliveredCount = orders.filter((o) => o.items.every((i) => i.status === 'delivered')).length; + + return ( +
+
+ + + +
+ +
+

Active Orders

+
+ {orders + .filter((order) => order.items.some((i) => i.status !== 'delivered')) + .map((order) => ( + + ))} +
+
+
+ ); +} diff --git a/src/app/u/[locationId]/live/_components/OpenOrdersList.new.tsx b/src/app/u/[locationId]/live/_components/OpenOrdersList.new.tsx new file mode 100644 index 0000000..a3e9803 --- /dev/null +++ b/src/app/u/[locationId]/live/_components/OpenOrdersList.new.tsx @@ -0,0 +1,84 @@ +import { LayoutDashboard } from 'lucide-react'; +import { EmptyState } from '~/app/u/[locationId]/_components/EmptyState'; +import { LiveOrders } from '~/app/u/[locationId]/live/_components/LiveOrders'; +import { type LocationId } from '~/domain/locations'; +import { type OrderItemStatus } from '~/domain/order-items'; +import { getUsedFeatureQuota } from '~/lib/quota-utils.server-only'; +import { ROUTES } from '~/lib/routes'; +import { db } from '~/server/db'; + +export async function OpenOrdersList(props: { locationId: LocationId }) { + // Fetch all non-completed orders for this location + type OrderWithDetails = { + id: number; + locationId: number; + orderItems: Array<{ + id: number; + orderId: number; + menuItemId: number; + status: string; + menuItem: { + id: number; + name: string | null; + price: string; + }; + }>; + createdAt: Date; + updatedAt: Date | null; + }; + + const openOrders = (await db.query.orders.findMany({ + where: (orders, { eq }) => eq(orders.locationId, props.locationId), + with: { + orderItems: { + columns: { + id: true, + status: true, + orderId: true, + menuItemId: true, + }, + with: { + menuItem: { + columns: { + id: true, + name: true, + price: true, + }, + }, + }, + }, + }, + })) as OrderWithDetails[]; + + const ordersWithItems = openOrders.map((order) => ({ + ...order, + orderId: order.id.toString(), + items: order.orderItems.map((item) => ({ + menuItem: { + id: item.menuItem.id, + name: item.menuItem.name ?? '', + price: item.menuItem.price, + }, + status: item.status as OrderItemStatus, + })), + })); + + if (ordersWithItems.length === 0) { + const hasAddedMenus = (await getUsedFeatureQuota('menus')) > 0; + const title = 'No open orders at the moment'; + const secondary = hasAddedMenus + ? 'Please come back in a while.' + : 'For orders to flow in, start by adding one or more menus.'; + return ( + } + title={title} + secondary={secondary} + cta={hasAddedMenus ? undefined : 'Add menu'} + ctaHref={hasAddedMenus ? undefined : ROUTES.menusAdd(props.locationId)} + /> + ); + } + + return ; +} diff --git a/src/app/u/[locationId]/live/_components/OpenOrdersList.tsx b/src/app/u/[locationId]/live/_components/OpenOrdersList.tsx index 21c364c..3369d2d 100644 --- a/src/app/u/[locationId]/live/_components/OpenOrdersList.tsx +++ b/src/app/u/[locationId]/live/_components/OpenOrdersList.tsx @@ -1,29 +1,96 @@ +import { type InferSelectModel } from 'drizzle-orm'; import { LayoutDashboard } from 'lucide-react'; import { EmptyState } from '~/app/u/[locationId]/_components/EmptyState'; +import { LiveOrders } from '~/app/u/[locationId]/live/_components/LiveOrders'; import { type LocationId } from '~/domain/locations'; +import { type OrderItemStatus } from '~/domain/order-items'; +import { AppError } from '~/lib/error-utils.server'; import { getUsedFeatureQuota } from '~/lib/quota-utils.server-only'; import { ROUTES } from '~/lib/routes'; +import { db } from '~/server/db'; +import { type menuItems, type orderItems, type orders } from '~/server/db/schema'; +import { getOpenOrdersByLocation } from '~/server/queries/orders'; + +type DBOrderItem = InferSelectModel & { + menuItem: InferSelectModel; +}; + +type DBOrder = InferSelectModel & { + orderItems: DBOrderItem[]; +}; export async function OpenOrdersList(props: { locationId: LocationId }) { - //TODO: Fetch actual open orders - const items = await Promise.resolve([]); - - if (items.length === 0) { - const hasAddedMenus = (await getUsedFeatureQuota('menus')) > 0; - const title = 'No open orders at the moment'; - const secondary = hasAddedMenus - ? 'Please come back in a while.' - : 'For orders to flow in, start by adding one or more menus.'; - return ( - } - title={title} - secondary={secondary} - cta={hasAddedMenus ? undefined : 'Add menu'} - ctaHref={hasAddedMenus ? undefined : ROUTES.menusAdd(props.locationId)} - /> + try { + const openOrders = await getOpenOrdersByLocation(props.locationId); + + if (!openOrders) { + throw new AppError({ + internalMessage: `Failed to fetch open orders for location ${props.locationId}`, + publicMessage: 'Failed to load orders. Please try refreshing the page.', + }); + } + + // Fetch all menu items related to the order items + const menuItemIds = openOrders.flatMap((order) => order.orderItems.map((item) => item.menuItemId)); + + const menuItemsData = (await db.query.menuItems.findMany({ + where: (menuItems, { inArray }) => inArray(menuItems.id, menuItemIds), + columns: { + id: true, + name: true, + price: true, + }, + })) as InferSelectModel[]; + + // Create a map of menuItemId to menuItem for quick lookup + const menuItemsMap = new Map>( + menuItemsData.map((item) => [item.id, item]), ); - } - return
Open orders
; + const ordersWithItems = openOrders.map((order) => ({ + ...order, + orderId: order.id.toString(), + items: order.orderItems.map((item) => { + const menuItem = menuItemsMap.get(item.menuItemId); + if (!menuItem) { + console.warn(`Menu item with ID ${item.menuItemId} not found for order item ${item.id}`); + } + return { + menuItem: { + id: menuItem?.id ?? item.menuItemId, // Fallback to the menuItemId if not found + name: menuItem?.name ?? 'Unknown Item', + price: menuItem?.price ?? '0', // Default price if not found + }, + status: item.status as OrderItemStatus, + }; + }), + })); + + if (ordersWithItems.length === 0) { + const hasAddedMenus = (await getUsedFeatureQuota('menus')) > 0; + const title = 'No open orders at the moment'; + const secondary = hasAddedMenus + ? 'Please come back in a while.' + : 'For orders to flow in, start by adding one or more menus.'; + return ( + } + title={title} + secondary={secondary} + cta={hasAddedMenus ? undefined : 'Add menu'} + ctaHref={hasAddedMenus ? undefined : ROUTES.menusAdd(props.locationId)} + /> + ); + } + + return ; + } catch (error) { + console.error('Error in OpenOrdersList:', error); + throw error instanceof AppError + ? error + : new AppError({ + internalMessage: `Unexpected error in OpenOrdersList: ${error}`, + publicMessage: 'Failed to load orders. Please try refreshing the page.', + }); + } } diff --git a/src/app/u/[locationId]/live/_components/OrderCard.tsx b/src/app/u/[locationId]/live/_components/OrderCard.tsx new file mode 100644 index 0000000..5953e56 --- /dev/null +++ b/src/app/u/[locationId]/live/_components/OrderCard.tsx @@ -0,0 +1,65 @@ +'use client'; + +import { Check, Clock } from 'lucide-react'; +import { useState } from 'react'; +import { Button } from '~/components/ui/button'; +import { Card } from '~/components/ui/card'; +import { type LocationId } from '~/domain/locations'; +import { type OrderWithItems } from '~/domain/orders'; +import { updateOrderItemStatus } from '../_actions/updateOrderItemStatus'; + +export function OrderCard({ order, locationId }: { order: OrderWithItems; locationId: LocationId }) { + const [isUpdating, setIsUpdating] = useState(false); + + const handleUpdateStatus = async (itemId: number, newStatus: 'ordered' | 'delivered') => { + try { + setIsUpdating(true); + await updateOrderItemStatus(locationId, order.orderId!, itemId, newStatus); + } catch (error) { + console.error('Failed to update order status:', error); + } finally { + setIsUpdating(false); + } + }; + + return ( + +
+
+

Order #{order.orderId}

+

{new Date(order.createdAt).toLocaleTimeString()}

+
+
+ +
+ {order.items.map((item, index) => ( +
+
+

{item.menuItem.name}

+

${item.menuItem.price}

+
+
+ {item.status === 'ordered' && ( + <> + + + + )} + {item.status === 'delivered' && } +
+
+ ))} +
+
+ ); +} diff --git a/src/components/Spinner.tsx b/src/components/Spinner.tsx new file mode 100644 index 0000000..b0cc2f0 --- /dev/null +++ b/src/components/Spinner.tsx @@ -0,0 +1,7 @@ +export function Spinner() { + return ( +
+
+
+ ); +} diff --git a/src/components/forms/ReactHookFormField.tsx b/src/components/forms/ReactHookFormField.tsx index 1adc833..7e7c910 100644 --- a/src/components/forms/ReactHookFormField.tsx +++ b/src/components/forms/ReactHookFormField.tsx @@ -7,9 +7,16 @@ import { ReactHookFormLabelWithCharCounter } from '~/components/forms/ReactHookF import { FormControl, FormDescription, FormField, FormItem, FormMessage } from '~/components/ui/form'; import { Input } from '~/components/ui/input'; +interface ZodMaxCheck { + kind: 'max'; + value: number; + message?: string; +} + function getMaxLength(schema?: ZodString | ZodOptional): number { const stringSchema = schema instanceof ZodOptional ? schema.unwrap() : schema; - return stringSchema?._def?.checks?.find((check) => check.kind === 'max')?.value ?? 0; + const maxCheck = stringSchema?._def?.checks?.find((check): check is ZodMaxCheck => check.kind === 'max'); + return maxCheck?.value ?? 0; } export function ReactHookFormField(props: { schema: ZodObject; form: any; fieldName: string }) { diff --git a/src/domain/orders.ts b/src/domain/orders.ts index 6e2d40a..409fef2 100644 --- a/src/domain/orders.ts +++ b/src/domain/orders.ts @@ -6,6 +6,10 @@ import { orders } from '~/server/db/schema'; export const PREPAID_STATUSES = ['draft', 'paid'] as const; export type Order = InferSelectModel; +export type OrderWithItems = Order & { + items: PublicOrderItem[]; + orderId?: string; +}; export const orderIdSchema = z.coerce.string(); export type OrderId = z.infer; diff --git a/src/domain/session.ts b/src/domain/session.ts new file mode 100644 index 0000000..79e7226 --- /dev/null +++ b/src/domain/session.ts @@ -0,0 +1,9 @@ +import { type PriceTierId } from './price-tiers'; + +export interface CustomJwtSessionClaims { + metadata?: { + tier?: PriceTierId; + orgId?: string; + orgName?: string; + }; +} diff --git a/src/env.js b/src/env.js index 0a7151b..7033efd 100644 --- a/src/env.js +++ b/src/env.js @@ -12,22 +12,24 @@ export const env = createEnv({ /** * Specify your server-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. - */ - server: { + */ server: { POSTGRES_URL: z.string().url(), STRIPE_SECRET_KEY: z.string(), SENTRY_AUTH_TOKEN: z.string(), POSTHOG_ANALYTICS_QUERIES_API_KEY: z.string(), POSTHOG_PROJECT_ID: z.string(), NODE_ENV: z.enum(['development', 'test', 'production']).default('development'), + PUSHER_APP_ID: z.string(), + PUSHER_APP_KEY: z.string(), + PUSHER_APP_SECRET: z.string(), + PUSHER_CLUSTER: z.string(), }, /** * Specify your client-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. To expose them to the client, prefix them with * `NEXT_PUBLIC_`. - */ - client: { + */ client: { NEXT_PUBLIC_APP_URL: z.string().url(), NEXT_PUBLIC_ENV: z.string(), NEXT_PUBLIC_LOG_TO_SENTRY: z.enum(['yes', 'no']), @@ -35,6 +37,8 @@ export const env = createEnv({ NEXT_PUBLIC_SENTRY_DSN: z.string(), NEXT_PUBLIC_SENTRY_ORG: z.string(), NEXT_PUBLIC_SENTRY_PROJECT: z.string(), + NEXT_PUBLIC_PUSHER_APP_KEY: z.string(), + NEXT_PUBLIC_PUSHER_CLUSTER: z.string(), NEXT_PUBLIC_CLERK_SIGN_UP_URL: z.string(), NEXT_PUBLIC_CLERK_SIGN_UP_FORCE_REDIRECT_URL: z.string(), NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL: z.string(), @@ -54,8 +58,7 @@ export const env = createEnv({ /** * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. * middlewares) or client-side so we need to destruct manually. - */ - runtimeEnv: { + */ runtimeEnv: { NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL, NEXT_PUBLIC_ENV: process.env.NEXT_PUBLIC_ENV, POSTGRES_URL: process.env.POSTGRES_URL, @@ -64,6 +67,12 @@ export const env = createEnv({ NODE_ENV: process.env.NODE_ENV, NEXT_PUBLIC_LOG_TO_SENTRY: process.env.NEXT_PUBLIC_LOG_TO_SENTRY, NEXT_PUBLIC_LOG_TO_CONSOLE: process.env.NEXT_PUBLIC_LOG_TO_CONSOLE, + PUSHER_APP_ID: process.env.PUSHER_APP_ID, + PUSHER_APP_KEY: process.env.PUSHER_APP_KEY, + PUSHER_APP_SECRET: process.env.PUSHER_APP_SECRET, + PUSHER_CLUSTER: process.env.PUSHER_CLUSTER, + NEXT_PUBLIC_PUSHER_APP_KEY: process.env.NEXT_PUBLIC_PUSHER_APP_KEY, + NEXT_PUBLIC_PUSHER_CLUSTER: process.env.NEXT_PUBLIC_PUSHER_CLUSTER, NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN, NEXT_PUBLIC_SENTRY_ORG: process.env.NEXT_PUBLIC_SENTRY_ORG, NEXT_PUBLIC_SENTRY_PROJECT: process.env.NEXT_PUBLIC_SENTRY_PROJECT, diff --git a/src/hooks/use-real-time.ts b/src/hooks/use-real-time.ts new file mode 100644 index 0000000..611e80e --- /dev/null +++ b/src/hooks/use-real-time.ts @@ -0,0 +1,83 @@ +import { useAtom } from 'jotai'; +import { useEffect } from 'react'; +import { orderAtom, type PublicOrder } from '~/app/p/[locationSlug]/_state/cart'; +import { type OrderWithItems } from '~/domain/orders'; +import { useToast } from '~/hooks/use-toast'; +import { CHANNELS, EVENTS, pusherClient } from '~/lib/pusher'; + +const convertToPublicOrder = (order: OrderWithItems, currencyId: string = 'USD'): PublicOrder => ({ + locationId: order.locationId, + orderId: order.orderId ?? order.id.toString(), + items: order.items, + currencyId: currencyId as PublicOrder['currencyId'], +}); + +export function useRealTimeOrderUpdates(orderId: string | undefined, locationId: number) { + const [order, setOrder] = useAtom(orderAtom); + const { toast } = useToast(); + + useEffect(() => { + if (!orderId || !locationId) return; + + // Subscribe to both location-wide and order-specific channels + const locationChannel = pusherClient.subscribe(CHANNELS.location(locationId)); + const orderChannel = pusherClient.subscribe(CHANNELS.order(orderId)); + + // Handle new orders in the location + locationChannel.bind(EVENTS.ORDER_CREATED, (data: OrderWithItems) => { + toast({ + title: 'New Order', + description: `Order #${orderId} has been created`, + }); + }); + + // Handle updates to the current order + orderChannel.bind(EVENTS.ORDER_UPDATED, (data: OrderWithItems) => { + setOrder(convertToPublicOrder(data, order.currencyId)); + toast({ + title: 'Order Updated', + description: `Order #${orderId} has been updated`, + }); + }); + + // Handle updates to order items + orderChannel.bind(EVENTS.ORDER_ITEM_UPDATED, (data: OrderWithItems) => { + setOrder(convertToPublicOrder(data, order.currencyId)); + }); + + return () => { + locationChannel.unsubscribe(); + orderChannel.unsubscribe(); + }; + }, [orderId, locationId, toast, setOrder, order.currencyId]); +} + +export function useRealTimeLocationUpdates(locationId: number) { + const { toast } = useToast(); + + useEffect(() => { + if (!locationId) return; + + const locationChannel = pusherClient.subscribe(CHANNELS.location(locationId)); + + // Handle new orders in the location + locationChannel.bind(EVENTS.ORDER_CREATED, (data: OrderWithItems) => { + toast({ + title: 'New Order', + description: `Order #${data.orderId ?? data.id} has been created`, + }); + }); + + // Handle updates to any order in the location + locationChannel.bind(EVENTS.ORDER_UPDATED, (data: OrderWithItems) => { + toast({ + title: 'Order Updated', + description: `Order #${data.orderId ?? data.id} has been updated`, + }); + }); + + return () => { + locationChannel.unsubscribe(); + }; + }, [locationId, toast]); +} diff --git a/src/lib/clerk-theme.ts b/src/lib/clerk-theme.ts new file mode 100644 index 0000000..161e065 --- /dev/null +++ b/src/lib/clerk-theme.ts @@ -0,0 +1,21 @@ +export const clerkTheme = { + variables: { + colorPrimary: '#34D399', // Emerald-400 + colorText: '#111827', // Gray-900 + colorBackground: '#FFFFFF', + colorDanger: '#EF4444', // Red-500 + colorSuccess: '#10B981', // Emerald-500 + colorWarning: '#F59E0B', // Amber-500 + colorInputText: '#374151', // Gray-700 + colorInputBackground: '#F3F4F6', // Gray-100 + colorAlphaShade: '#6B7280', // Gray-500 + }, + elements: { + formButtonPrimary: { + backgroundColor: '#34D399', + '&:hover': { + backgroundColor: '#10B981', + }, + }, + }, +}; diff --git a/src/lib/pusher.ts b/src/lib/pusher.ts new file mode 100644 index 0000000..0fd376e --- /dev/null +++ b/src/lib/pusher.ts @@ -0,0 +1,36 @@ +import PusherServer from 'pusher'; +import PusherClient from 'pusher-js'; +import { env } from '~/env'; + +// Server-side Pusher instance (only import in server components) +export const getPusherServer = () => { + if (typeof window !== 'undefined') { + throw new Error('getPusherServer should only be called on the server side'); + } + return new PusherServer({ + appId: env.PUSHER_APP_ID, + key: env.PUSHER_APP_KEY, + secret: env.PUSHER_APP_SECRET, + cluster: env.PUSHER_CLUSTER, + useTLS: true, + }); +}; + +// Client-side Pusher instance (safe to import anywhere) +export const pusherClient = new PusherClient(process.env.NEXT_PUBLIC_PUSHER_APP_KEY!, { + cluster: process.env.NEXT_PUBLIC_PUSHER_CLUSTER!, +}); + +// Event types +export const EVENTS = { + ORDER_CREATED: 'order:created', + ORDER_UPDATED: 'order:updated', + ORDER_ITEM_CREATED: 'order-item:created', + ORDER_ITEM_UPDATED: 'order-item:updated', +} as const; + +// Channel types +export const CHANNELS = { + location: (locationId: number) => `location-${locationId}`, + order: (orderId: string) => `order-${orderId}`, +} as const; diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts index 19970f4..38ae974 100644 --- a/src/server/db/schema.ts +++ b/src/server/db/schema.ts @@ -1,7 +1,7 @@ // Example model schema from the Drizzle docs // https://orm.drizzle.team/docs/sql-schema-declaration -import { sql } from 'drizzle-orm'; +import { relations, sql } from 'drizzle-orm'; import { boolean, decimal, index, integer, pgTableCreator, primaryKey, timestamp, varchar } from 'drizzle-orm/pg-core'; import { MENU_MODES, MenuModeId } from '~/domain/menu-modes'; import { ORDER_ITEM_STATUSES, OrderItemStatus } from '~/domain/order-items'; @@ -174,3 +174,15 @@ export const orderItems = createTable( }, ], ); + +// optional: add relations for `.with` +export const ordersRelations = relations(orders, ({ many }) => ({ + orderItems: many(orderItems), +})); + +export const orderItemsRelations = relations(orderItems, ({ one }) => ({ + order: one(orders, { + fields: [orderItems.orderId], + references: [orders.id], + }), +})); diff --git a/src/server/queries/orders.ts b/src/server/queries/orders.ts index 0e9b422..7d19649 100644 --- a/src/server/queries/orders.ts +++ b/src/server/queries/orders.ts @@ -1,10 +1,12 @@ -import { sql } from 'drizzle-orm'; +import { sql, type InferSelectModel } from 'drizzle-orm'; import { z } from 'zod'; +import type { LocationId } from '~/domain/locations'; import { OrderItemStatus } from '~/domain/order-items'; import { orderFormSchema } from '~/domain/orders'; import { AppError } from '~/lib/error-utils.server'; import { db } from '~/server/db'; import { orderItems, orders } from '~/server/db/schema'; +import { getLocationForCurrentUserOrThrow } from '~/server/queries/locations'; // function generateUniqueOrderNumber(): string { // const timestamp = new Date().getTime().toString(36).toUpperCase(); @@ -44,3 +46,19 @@ export async function createOrder(data: z.infer) { return order; }); } + +type OrderWithItems = InferSelectModel & { + orderItems: Array>; +}; + +export async function getOpenOrdersByLocation(locationId: LocationId): Promise { + const validLocation = await getLocationForCurrentUserOrThrow(locationId); + const items = await db.query.orders.findMany({ + where: (orders, { eq }) => eq(orders.locationId, validLocation.id), + with: { + orderItems: true, // fetch all columns of each orderItem + }, + }); + + return items; +} diff --git a/tsconfig.json b/tsconfig.json index 2f4197a..6d0b4f2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,43 +1,51 @@ { - "compilerOptions": { - /* Base Options: */ - "esModuleInterop": true, - "skipLibCheck": true, - "target": "es2022", - "allowJs": true, - "resolveJsonModule": true, - "moduleDetection": "force", - "isolatedModules": true, - - /* Strictness */ - "strict": true, - "noUncheckedIndexedAccess": true, - "checkJs": true, - - /* Bundled projects */ - "lib": ["dom", "dom.iterable", "ES2022"], - "noEmit": true, - "module": "ESNext", - "moduleResolution": "Bundler", - "jsx": "preserve", - "plugins": [{ "name": "next" }], - "incremental": true, - - /* Path Aliases */ - "baseUrl": ".", - "paths": { - "~/*": ["./src/*"] - } - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - "**/*.js", - "**/*.config.js", - "**/*.config.ts", - ".next/types/**/*.ts", - "src/components/ErrorCard.test.tsx" + "compilerOptions": { + /* Base Options: */ + "esModuleInterop": true, + "skipLibCheck": true, + "target": "es2022", + "allowJs": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, + /* Strictness */ + "strict": true, + "noUncheckedIndexedAccess": true, + "checkJs": true, + /* Bundled projects */ + "lib": [ + "dom", + "dom.iterable", + "ES2022" ], - "exclude": ["node_modules", ".next", "out", "storybook-static"] + "noEmit": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "jsx": "preserve", + "plugins": [ + { + "name": "next" + } + ], + "incremental": true, + /* Path Aliases */ + "baseUrl": ".", + "paths": { + "~/*": [ + "./src/*" + ] + } + }, + "include": [ + "**/*.cjs", + "**/*.mjs", + "**/*.ts", + "**/*.tsx", + ".eslintrc.cjs", + "next-env.d.ts", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] } From 704a5b5b56722ef8958e5bcd5164a33f84ab39ab Mon Sep 17 00:00:00 2001 From: dopoto Date: Sun, 18 May 2025 10:05:05 +0300 Subject: [PATCH 28/47] wip --- src/app/actions/createCartPaymentIntent.ts | 2 +- src/app/actions/placeOrderAction.ts | 14 +- src/app/api/realtime/notifications.ts | 8 +- .../_components/OrderItemsList.tsx | 3 +- .../_components/PublicFooterDrawer.tsx | 2 +- .../_components/PublicFooterPostpaidMode.tsx | 22 ++- .../_components/PublicFooterPrepaidMode.tsx | 4 + src/app/p/[locationSlug]/_state/cart.ts | 13 +- .../markOrderItemAsDeliveredAction.ts | 72 ++++++++ .../live/_actions/updateOrderItemStatus.ts | 102 ----------- .../live/_components/LiveOrders.tsx | 39 +++-- .../live/_components/OpenOrdersList.new.tsx | 158 +++++++++--------- .../live/_components/OpenOrdersList.tsx | 39 +---- .../live/_components/OrderCard.tsx | 38 +++-- src/components/AsyncComponent.tsx | 0 src/components/public/PublicMenuItem.tsx | 18 +- src/domain/order-items.ts | 23 ++- src/domain/orders.ts | 10 +- src/hooks/use-real-time.ts | 27 +-- src/lib/toast-utils.ts | 5 + src/server/db/schema.ts | 40 ++--- src/server/queries/orders.ts | 128 +++++++++++--- 22 files changed, 410 insertions(+), 357 deletions(-) create mode 100644 src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts create mode 100644 src/components/AsyncComponent.tsx create mode 100644 src/lib/toast-utils.ts diff --git a/src/app/actions/createCartPaymentIntent.ts b/src/app/actions/createCartPaymentIntent.ts index 676b815..1d2d7c5 100644 --- a/src/app/actions/createCartPaymentIntent.ts +++ b/src/app/actions/createCartPaymentIntent.ts @@ -1,6 +1,6 @@ 'use server'; -import { PublicOrderItem } from '~/app/p/[locationSlug]/_state/cart'; +import { PublicOrderItem } from '~/domain/order-items'; import { type PaymentIntentResponse } from '~/domain/payments'; import { createPaymentIntent, verifyConnectAccount } from '~/lib/payment-utils'; import { getMenuItemsByLocation } from '~/server/queries/menu-items'; diff --git a/src/app/actions/placeOrderAction.ts b/src/app/actions/placeOrderAction.ts index d72f86b..1abdeea 100644 --- a/src/app/actions/placeOrderAction.ts +++ b/src/app/actions/placeOrderAction.ts @@ -5,14 +5,14 @@ import { headers } from 'next/headers'; import { z } from 'zod'; import { notifyOrderCreated } from '~/app/api/realtime/notifications'; import { menuFormSchema } from '~/domain/menus'; -import { orderFormSchema, type OrderWithItems } from '~/domain/orders'; +import { orderFormSchema, type PublicOrderWithItems } from '~/domain/orders'; import { AppError } from '~/lib/error-utils.server'; import { type FormState, processFormErrors } from '~/lib/form-state'; import { createOrder } from '~/server/queries/orders'; export const placeOrderAction = async ( data: z.infer, -): Promise> => { +): Promise> => { 'use server'; return await Sentry.withServerActionInstrumentation( 'placeOrderAction', @@ -34,19 +34,13 @@ export const placeOrderAction = async ( rootError: 'Out of quota for orders.', }; } - const order = await createOrder(parsedForm.data); + const orderWithItems = await createOrder(parsedForm.data); - // Send real-time notification - const orderWithItems: OrderWithItems = { - ...order, - orderId: order.id.toString(), - items: parsedForm.data.items, - }; await notifyOrderCreated(parsedForm.data.locationId, orderWithItems); return { status: 'success' as const, - fields: { orderId: order.id.toString() }, + fields: { orderWithItems }, }; } catch (error) { if (error instanceof AppError) { diff --git a/src/app/api/realtime/notifications.ts b/src/app/api/realtime/notifications.ts index 631f508..c6452ab 100644 --- a/src/app/api/realtime/notifications.ts +++ b/src/app/api/realtime/notifications.ts @@ -1,14 +1,14 @@ import { type LocationId } from '~/domain/locations'; -import { type OrderWithItems } from '~/domain/orders'; +import { type PublicOrderWithItems } from '~/domain/orders'; import { CHANNELS, EVENTS, getPusherServer } from '~/lib/pusher'; -export async function notifyOrderCreated(locationId: LocationId, order: OrderWithItems) { +export async function notifyOrderCreated(locationId: LocationId, order: PublicOrderWithItems) { const pusher = getPusherServer(); // Notify all clients listening to this location about the new order await pusher.trigger(CHANNELS.location(locationId), EVENTS.ORDER_CREATED, order); } -export async function notifyOrderUpdated(locationId: LocationId, order: OrderWithItems) { +export async function notifyOrderUpdated(locationId: LocationId, order: PublicOrderWithItems) { const pusher = getPusherServer(); // Notify all clients listening to this location about the order update await pusher.trigger(CHANNELS.location(locationId), EVENTS.ORDER_UPDATED, order); @@ -17,7 +17,7 @@ export async function notifyOrderUpdated(locationId: LocationId, order: OrderWit await pusher.trigger(CHANNELS.order(order.id.toString()), EVENTS.ORDER_UPDATED, order); } -export async function notifyOrderItemUpdated(locationId: LocationId, order: OrderWithItems) { +export async function notifyOrderItemUpdated(locationId: LocationId, order: PublicOrderWithItems) { const pusher = getPusherServer(); await pusher.trigger(CHANNELS.order(order.id.toString()), EVENTS.ORDER_ITEM_UPDATED, order); } diff --git a/src/app/p/[locationSlug]/_components/OrderItemsList.tsx b/src/app/p/[locationSlug]/_components/OrderItemsList.tsx index e0bbb9a..1632e62 100644 --- a/src/app/p/[locationSlug]/_components/OrderItemsList.tsx +++ b/src/app/p/[locationSlug]/_components/OrderItemsList.tsx @@ -1,6 +1,7 @@ import { useAtom } from 'jotai'; -import { orderAtom, PublicOrderItem } from '~/app/p/[locationSlug]/_state/cart'; +import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; import { CURRENCIES } from '~/domain/currencies'; +import { PublicOrderItem } from '~/domain/order-items'; export function OrderItemsList(props: { items: PublicOrderItem[] }) { const [order] = useAtom(orderAtom); diff --git a/src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx b/src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx index 883b507..3882851 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterDrawer.tsx @@ -28,7 +28,7 @@ export function PublicFooterDrawer(props: {
0 ? 'text-black' : 'text-gray-500'; @@ -48,22 +49,18 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati const res = await placeOrderAction(order); if (res.status === 'success') { - const orderId = res.fields?.orderId; + const orderWithItems = res.fields?.orderWithItems; toast({ title: 'Order placed successfully', - description: `Your order number is ${orderId}`, + description: `Your order number is ${orderWithItems?.id}`, variant: 'default', + className: getTopPositionedToast(), }); setOrder((prevOrder) => { return { ...prevOrder, - orderId, - items: prevOrder.items.map((item) => { - if (item.status === 'draft') { - return { ...item, status: 'ordered' }; - } - return item; - }), + orderId: orderWithItems?.id ? String(orderWithItems.id) : undefined, //TODO review + items: orderWithItems?.items ?? [], }; }); } else { @@ -71,6 +68,7 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati title: 'Failed to place order', description: 'Please try again', variant: 'destructive', + className: getTopPositionedToast(), }); } } catch (error) { @@ -80,9 +78,9 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati } }; - const draftItems = order.items.filter((item) => item.status === 'draft'); - const inPreparationItems = order.items.filter((item) => item.status === 'ordered'); - const deliveredItems = order.items.filter((item) => item.status === 'delivered'); + const draftItems = order.items.filter((item) => !item.orderItem.id); + const inPreparationItems = order.items.filter((item) => item.orderItem.id && item.orderItem.isDelivered === false); + const deliveredItems = order.items.filter((item) => item.orderItem.id && item.orderItem.isDelivered === true); const draftItemsSummary = ( diff --git a/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx index 24577da..121aef5 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx @@ -11,6 +11,7 @@ import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '~/co import { CURRENCIES, type CurrencyId } from '~/domain/currencies'; import { LocationId } from '~/domain/locations'; import { useToast } from '~/hooks/use-toast'; +import { getTopPositionedToast } from '~/lib/toast-utils'; export function PublicFooterPrepaidMode(props: { currencyId: CurrencyId; locationId: LocationId }) { const currency = CURRENCIES[props.currencyId]; @@ -34,6 +35,7 @@ export function PublicFooterPrepaidMode(props: { currencyId: CurrencyId; locatio toast({ title: 'Error', description: err instanceof Error ? err.message : 'Failed to initiate checkout', + className: getTopPositionedToast(), }); } finally { setIsLoading(false); @@ -44,6 +46,7 @@ export function PublicFooterPrepaidMode(props: { currencyId: CurrencyId; locatio toast({ title: 'Success', description: 'Payment completed successfully!', + className: getTopPositionedToast(), }); //setOrder( ); setIsCheckingOut(false); @@ -53,6 +56,7 @@ export function PublicFooterPrepaidMode(props: { currencyId: CurrencyId; locatio toast({ title: 'Payment Failed', description: error.message, + className: getTopPositionedToast(), }); setIsCheckingOut(false); }; diff --git a/src/app/p/[locationSlug]/_state/cart.ts b/src/app/p/[locationSlug]/_state/cart.ts index 27e2a84..c2323b0 100644 --- a/src/app/p/[locationSlug]/_state/cart.ts +++ b/src/app/p/[locationSlug]/_state/cart.ts @@ -1,16 +1,5 @@ import { atom } from 'jotai'; -import { z } from 'zod'; -import { CurrencyId } from '~/domain/currencies'; -import { OrderItemStatus } from '~/domain/order-items'; -import { orderFormSchema } from '~/domain/orders'; -import { type MenuItem } from '../../../../domain/menu-items'; - -export type PublicOrder = z.infer & { currencyId: CurrencyId }; - -export interface PublicOrderItem { - menuItem: Pick; - status: OrderItemStatus; -} +import { PublicOrder } from '~/domain/orders'; export const orderAtom = atom({ locationId: 0, currencyId: 'USD', items: [] }); orderAtom.debugLabel = 'orderAtom'; diff --git a/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts b/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts new file mode 100644 index 0000000..57d193b --- /dev/null +++ b/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts @@ -0,0 +1,72 @@ +'use server'; + +import { auth } from '@clerk/nextjs/server'; +import { eq } from 'drizzle-orm'; +import { revalidatePath } from 'next/cache'; +import { notifyOrderUpdated } from '~/app/api/realtime/notifications'; +import { type LocationId } from '~/domain/locations'; +import { AppError } from '~/lib/error-utils.server'; +import { db } from '~/server/db'; +import { orderItems } from '~/server/db/schema'; +import { getLocationForCurrentUserOrThrow } from '~/server/queries/locations'; +import { getOrderById } from '~/server/queries/orders'; + +// type OrderItem = InferSelectModel & { +// menuItem: InferSelectModel; +// }; + +export async function markOrderItemAsDeliveredAction(locationId: LocationId, orderItemId: number) { + const { userId } = await auth(); //TODO needed? + if (!userId) { + throw new AppError({ internalMessage: 'Unauthorized' }); + } + + // Verify that the location belongs to the current user's organization + await getLocationForCurrentUserOrThrow(locationId); + + // TODO Check that the order item id is in the order items for the location + + // Update the order item status + const [updatedItem] = await db + .update(orderItems) + .set({ + isDelivered: true, + updatedAt: new Date(), + }) + .where(eq(orderItems.id, orderItemId)) + .returning(); + + if (!updatedItem) { + throw new AppError({ internalMessage: 'Could not update order item' }); + } + + // Get the full updated order with items and menu items + //const foundOrders = await getOpenOrdersByLocation(locationId) + + // Send real-time notification + // Fetch the full order with its items after updating + const order = await getOrderById(locationId, updatedItem.orderId.toString()); + + if (!order) { + throw new AppError({ internalMessage: 'Order not found after update' }); + } + + await notifyOrderUpdated(locationId, { + ...order, + items: order.items.map((item) => ({ + menuItem: { + id: item.menuItem.id, + name: item.menuItem.name ?? '', + price: item.menuItem.price, + }, + orderItem: { + id: item.orderItem.id, + isDelivered: item.orderItem.isDelivered, + isPaid: item.orderItem.isPaid, + }, + })), + }); + + revalidatePath(`/u/${locationId}/live`); //TODO + return order; +} diff --git a/src/app/u/[locationId]/live/_actions/updateOrderItemStatus.ts b/src/app/u/[locationId]/live/_actions/updateOrderItemStatus.ts index a131df9..e69de29 100644 --- a/src/app/u/[locationId]/live/_actions/updateOrderItemStatus.ts +++ b/src/app/u/[locationId]/live/_actions/updateOrderItemStatus.ts @@ -1,102 +0,0 @@ -'use server'; - -import { auth } from '@clerk/nextjs/server'; -import { and, eq, type InferSelectModel } from 'drizzle-orm'; -import { revalidatePath } from 'next/cache'; -import { notifyOrderUpdated } from '~/app/api/realtime/notifications'; -import { type LocationId } from '~/domain/locations'; -import { type OrderItemStatus } from '~/domain/order-items'; -import { type OrderId } from '~/domain/orders'; -import { AppError } from '~/lib/error-utils.server'; -import { db } from '~/server/db'; -import { menuItems, orderItems, orders } from '~/server/db/schema'; -import { getLocationForCurrentUserOrThrow } from '~/server/queries/locations'; - -type OrderItem = InferSelectModel & { - menuItem: InferSelectModel; -}; - -type Order = InferSelectModel & { - orderItems: OrderItem[]; -}; - -export async function updateOrderItemStatus( - locationId: LocationId, - orderId: OrderId, - menuItemId: number, - status: OrderItemStatus, -) { - const { userId } = await auth(); - if (!userId) { - throw new AppError({ internalMessage: 'Unauthorized' }); - } - - // Parse orderId to number since it might come as string from the frontend - const orderIdNum = parseInt(orderId.toString(), 10); - if (isNaN(orderIdNum)) { - throw new AppError({ internalMessage: 'Invalid order ID' }); - } - - // Verify that the location belongs to the current user's organization - await getLocationForCurrentUserOrThrow(locationId); - - // Update the order item status - const [updatedItem] = await db - .update(orderItems) - .set({ - status, - updatedAt: new Date(), - }) - .where(and(eq(orderItems.orderId, orderIdNum), eq(orderItems.menuItemId, menuItemId))) - .returning(); - - if (!updatedItem) { - throw new AppError({ internalMessage: 'Could not update order item' }); - } - - // Get the full updated order with items and menu items - const foundOrders = (await db.query.orders.findMany({ - where: (orders, { eq }) => eq(orders.id, orderIdNum), - with: { - orderItems: { - columns: { - id: true, - status: true, - orderId: true, - menuItemId: true, - }, - with: { - menuItem: { - columns: { - id: true, - name: true, - price: true, - }, - }, - }, - }, - }, - })) as Order[]; - - const order = foundOrders[0]; - if (!order || !order.orderItems) { - throw new AppError({ internalMessage: 'Could not find order' }); - } - - // Send real-time notification - await notifyOrderUpdated(locationId, { - ...order, - orderId: order.id.toString(), - items: order.orderItems.map((item: OrderItem) => ({ - menuItem: { - id: item.menuItem.id, - name: item.menuItem.name ?? '', - price: item.menuItem.price, - }, - status: item.status as OrderItemStatus, - })), - }); - - revalidatePath(`/u/${locationId}/live`); - return order; -} diff --git a/src/app/u/[locationId]/live/_components/LiveOrders.tsx b/src/app/u/[locationId]/live/_components/LiveOrders.tsx index faa1103..49d0cef 100644 --- a/src/app/u/[locationId]/live/_components/LiveOrders.tsx +++ b/src/app/u/[locationId]/live/_components/LiveOrders.tsx @@ -1,21 +1,24 @@ 'use client'; +import type { InferSelectModel } from 'drizzle-orm'; import { useEffect, useState } from 'react'; -import { DashboardCard } from '~/app/u/[locationId]/_components/DashboardCard'; import { type LocationId } from '~/domain/locations'; -import { type OrderWithItems } from '~/domain/orders'; +import { type PublicOrderWithItems } from '~/domain/orders'; import { useToast } from '~/hooks/use-toast'; import { CHANNELS, EVENTS, pusherClient } from '~/lib/pusher'; +import { menuItems } from '~/server/db/schema'; import { OrderCard } from './OrderCard'; export function LiveOrders({ locationId, initialOrders = [], + menuItemsMap, }: { locationId: LocationId; - initialOrders: OrderWithItems[]; + initialOrders: PublicOrderWithItems[]; + menuItemsMap: Map>; }) { - const [orders, setOrders] = useState(initialOrders); + const [orders, setOrders] = useState(initialOrders); const { toast } = useToast(); // Subscribe to location-wide order updates @@ -23,17 +26,17 @@ export function LiveOrders({ const locationChannel = pusherClient.subscribe(CHANNELS.location(locationId)); // Handle new orders - locationChannel.bind(EVENTS.ORDER_CREATED, (data: OrderWithItems) => { + locationChannel.bind(EVENTS.ORDER_CREATED, (data: PublicOrderWithItems) => { setOrders((current) => [...current, data]); toast({ title: 'New Order Received', - description: `Order #${data.orderId} has been created`, + description: `Order #${data.id} has been created`, }); }); // Handle updates to any order - locationChannel.bind(EVENTS.ORDER_UPDATED, (data: OrderWithItems) => { - setOrders((current) => current.map((order) => (order.orderId === data.orderId ? data : order))); + locationChannel.bind(EVENTS.ORDER_UPDATED, (data: PublicOrderWithItems) => { + setOrders((current) => current.map((order) => (order.id === data.id ? data : order))); }); return () => { @@ -45,13 +48,13 @@ export function LiveOrders({ }; }, [locationId, toast]); - const orderedCount = orders.filter((o) => o.items.some((i) => i.status === 'ordered')).length; - const preparingCount = orderedCount; // For now they're the same - const deliveredCount = orders.filter((o) => o.items.every((i) => i.status === 'delivered')).length; + // const orderedCount = orders.filter((o) => o.items.some((i) => i.status === 'ordered')).length; + // const preparingCount = orderedCount; // For now they're the same + // const deliveredCount = orders.filter((o) => o.items.every((i) => i.status === 'delivered')).length; return (
-
+ {/*
-
+
*/}
-

Active Orders

{orders - .filter((order) => order.items.some((i) => i.status !== 'delivered')) + .filter((order) => order.items.some((i) => i.orderItem.isDelivered === false)) .map((order) => ( - + ))}
diff --git a/src/app/u/[locationId]/live/_components/OpenOrdersList.new.tsx b/src/app/u/[locationId]/live/_components/OpenOrdersList.new.tsx index a3e9803..fcf4054 100644 --- a/src/app/u/[locationId]/live/_components/OpenOrdersList.new.tsx +++ b/src/app/u/[locationId]/live/_components/OpenOrdersList.new.tsx @@ -1,84 +1,84 @@ -import { LayoutDashboard } from 'lucide-react'; -import { EmptyState } from '~/app/u/[locationId]/_components/EmptyState'; -import { LiveOrders } from '~/app/u/[locationId]/live/_components/LiveOrders'; -import { type LocationId } from '~/domain/locations'; -import { type OrderItemStatus } from '~/domain/order-items'; -import { getUsedFeatureQuota } from '~/lib/quota-utils.server-only'; -import { ROUTES } from '~/lib/routes'; -import { db } from '~/server/db'; +// import { LayoutDashboard } from 'lucide-react'; +// import { EmptyState } from '~/app/u/[locationId]/_components/EmptyState'; +// import { LiveOrders } from '~/app/u/[locationId]/live/_components/LiveOrders'; +// import { type LocationId } from '~/domain/locations'; +// import { type OrderItemStatus } from '~/domain/order-items'; +// import { getUsedFeatureQuota } from '~/lib/quota-utils.server-only'; +// import { ROUTES } from '~/lib/routes'; +// import { db } from '~/server/db'; -export async function OpenOrdersList(props: { locationId: LocationId }) { - // Fetch all non-completed orders for this location - type OrderWithDetails = { - id: number; - locationId: number; - orderItems: Array<{ - id: number; - orderId: number; - menuItemId: number; - status: string; - menuItem: { - id: number; - name: string | null; - price: string; - }; - }>; - createdAt: Date; - updatedAt: Date | null; - }; +// export async function OpenOrdersList(props: { locationId: LocationId }) { +// // Fetch all non-completed orders for this location +// type OrderWithDetails = { +// id: number; +// locationId: number; +// orderItems: Array<{ +// id: number; +// orderId: number; +// menuItemId: number; +// status: string; +// menuItem: { +// id: number; +// name: string | null; +// price: string; +// }; +// }>; +// createdAt: Date; +// updatedAt: Date | null; +// }; - const openOrders = (await db.query.orders.findMany({ - where: (orders, { eq }) => eq(orders.locationId, props.locationId), - with: { - orderItems: { - columns: { - id: true, - status: true, - orderId: true, - menuItemId: true, - }, - with: { - menuItem: { - columns: { - id: true, - name: true, - price: true, - }, - }, - }, - }, - }, - })) as OrderWithDetails[]; +// const openOrders = (await db.query.orders.findMany({ +// where: (orders, { eq }) => eq(orders.locationId, props.locationId), +// with: { +// orderItems: { +// columns: { +// id: true, +// status: true, +// orderId: true, +// menuItemId: true, +// }, +// with: { +// menuItem: { +// columns: { +// id: true, +// name: true, +// price: true, +// }, +// }, +// }, +// }, +// }, +// })) as OrderWithDetails[]; - const ordersWithItems = openOrders.map((order) => ({ - ...order, - orderId: order.id.toString(), - items: order.orderItems.map((item) => ({ - menuItem: { - id: item.menuItem.id, - name: item.menuItem.name ?? '', - price: item.menuItem.price, - }, - status: item.status as OrderItemStatus, - })), - })); +// const ordersWithItems = openOrders.map((order) => ({ +// ...order, +// orderId: order.id.toString(), +// items: order.orderItems.map((item) => ({ +// menuItem: { +// id: item.menuItem.id, +// name: item.menuItem.name ?? '', +// price: item.menuItem.price, +// }, +// status: item.status as OrderItemStatus, +// })), +// })); - if (ordersWithItems.length === 0) { - const hasAddedMenus = (await getUsedFeatureQuota('menus')) > 0; - const title = 'No open orders at the moment'; - const secondary = hasAddedMenus - ? 'Please come back in a while.' - : 'For orders to flow in, start by adding one or more menus.'; - return ( - } - title={title} - secondary={secondary} - cta={hasAddedMenus ? undefined : 'Add menu'} - ctaHref={hasAddedMenus ? undefined : ROUTES.menusAdd(props.locationId)} - /> - ); - } +// if (ordersWithItems.length === 0) { +// const hasAddedMenus = (await getUsedFeatureQuota('menus')) > 0; +// const title = 'No open orders at the moment'; +// const secondary = hasAddedMenus +// ? 'Please come back in a while.' +// : 'For orders to flow in, start by adding one or more menus.'; +// return ( +// } +// title={title} +// secondary={secondary} +// cta={hasAddedMenus ? undefined : 'Add menu'} +// ctaHref={hasAddedMenus ? undefined : ROUTES.menusAdd(props.locationId)} +// /> +// ); +// } - return ; -} +// return ; +// } diff --git a/src/app/u/[locationId]/live/_components/OpenOrdersList.tsx b/src/app/u/[locationId]/live/_components/OpenOrdersList.tsx index 3369d2d..0f794e6 100644 --- a/src/app/u/[locationId]/live/_components/OpenOrdersList.tsx +++ b/src/app/u/[locationId]/live/_components/OpenOrdersList.tsx @@ -3,26 +3,16 @@ import { LayoutDashboard } from 'lucide-react'; import { EmptyState } from '~/app/u/[locationId]/_components/EmptyState'; import { LiveOrders } from '~/app/u/[locationId]/live/_components/LiveOrders'; import { type LocationId } from '~/domain/locations'; -import { type OrderItemStatus } from '~/domain/order-items'; import { AppError } from '~/lib/error-utils.server'; import { getUsedFeatureQuota } from '~/lib/quota-utils.server-only'; import { ROUTES } from '~/lib/routes'; import { db } from '~/server/db'; -import { type menuItems, type orderItems, type orders } from '~/server/db/schema'; +import { type menuItems } from '~/server/db/schema'; import { getOpenOrdersByLocation } from '~/server/queries/orders'; -type DBOrderItem = InferSelectModel & { - menuItem: InferSelectModel; -}; - -type DBOrder = InferSelectModel & { - orderItems: DBOrderItem[]; -}; - export async function OpenOrdersList(props: { locationId: LocationId }) { try { const openOrders = await getOpenOrdersByLocation(props.locationId); - if (!openOrders) { throw new AppError({ internalMessage: `Failed to fetch open orders for location ${props.locationId}`, @@ -30,9 +20,7 @@ export async function OpenOrdersList(props: { locationId: LocationId }) { }); } - // Fetch all menu items related to the order items - const menuItemIds = openOrders.flatMap((order) => order.orderItems.map((item) => item.menuItemId)); - + const menuItemIds = openOrders.flatMap((order) => order.items.map((item) => item.menuItem.id)); const menuItemsData = (await db.query.menuItems.findMany({ where: (menuItems, { inArray }) => inArray(menuItems.id, menuItemIds), columns: { @@ -47,26 +35,7 @@ export async function OpenOrdersList(props: { locationId: LocationId }) { menuItemsData.map((item) => [item.id, item]), ); - const ordersWithItems = openOrders.map((order) => ({ - ...order, - orderId: order.id.toString(), - items: order.orderItems.map((item) => { - const menuItem = menuItemsMap.get(item.menuItemId); - if (!menuItem) { - console.warn(`Menu item with ID ${item.menuItemId} not found for order item ${item.id}`); - } - return { - menuItem: { - id: menuItem?.id ?? item.menuItemId, // Fallback to the menuItemId if not found - name: menuItem?.name ?? 'Unknown Item', - price: menuItem?.price ?? '0', // Default price if not found - }, - status: item.status as OrderItemStatus, - }; - }), - })); - - if (ordersWithItems.length === 0) { + if (openOrders.length === 0) { const hasAddedMenus = (await getUsedFeatureQuota('menus')) > 0; const title = 'No open orders at the moment'; const secondary = hasAddedMenus @@ -83,7 +52,7 @@ export async function OpenOrdersList(props: { locationId: LocationId }) { ); } - return ; + return ; } catch (error) { console.error('Error in OpenOrdersList:', error); throw error instanceof AppError diff --git a/src/app/u/[locationId]/live/_components/OrderCard.tsx b/src/app/u/[locationId]/live/_components/OrderCard.tsx index 5953e56..e52e3f2 100644 --- a/src/app/u/[locationId]/live/_components/OrderCard.tsx +++ b/src/app/u/[locationId]/live/_components/OrderCard.tsx @@ -1,22 +1,32 @@ 'use client'; +import type { InferSelectModel } from 'drizzle-orm'; import { Check, Clock } from 'lucide-react'; import { useState } from 'react'; import { Button } from '~/components/ui/button'; import { Card } from '~/components/ui/card'; import { type LocationId } from '~/domain/locations'; -import { type OrderWithItems } from '~/domain/orders'; -import { updateOrderItemStatus } from '../_actions/updateOrderItemStatus'; +import { type PublicOrderWithItems } from '~/domain/orders'; +import { menuItems } from '~/server/db/schema'; +import { markOrderItemAsDeliveredAction } from '../_actions/markOrderItemAsDeliveredAction'; -export function OrderCard({ order, locationId }: { order: OrderWithItems; locationId: LocationId }) { +export function OrderCard({ + order, + locationId, + menuItemsMap, +}: { + order: PublicOrderWithItems; + locationId: LocationId; + menuItemsMap: Map>; +}) { const [isUpdating, setIsUpdating] = useState(false); - const handleUpdateStatus = async (itemId: number, newStatus: 'ordered' | 'delivered') => { + const markAsDelivered = async (orderItemId: number) => { try { setIsUpdating(true); - await updateOrderItemStatus(locationId, order.orderId!, itemId, newStatus); + await markOrderItemAsDeliveredAction(locationId, orderItemId); } catch (error) { - console.error('Failed to update order status:', error); + console.error('Failed to mark as delivered:', error); } finally { setIsUpdating(false); } @@ -26,7 +36,7 @@ export function OrderCard({ order, locationId }: { order: OrderWithItems; locati
-

Order #{order.orderId}

+

Order #{order.id}

{new Date(order.createdAt).toLocaleTimeString()}

@@ -34,28 +44,30 @@ export function OrderCard({ order, locationId }: { order: OrderWithItems; locati
{order.items.map((item, index) => (
-

{item.menuItem.name}

-

${item.menuItem.price}

+

{menuItemsMap.get(item.menuItem.id)?.name ?? 'Unknown Item'}

+

+ ${menuItemsMap.get(item.menuItem.id)?.price ?? 'Unknown Item'} +

- {item.status === 'ordered' && ( + {item.orderItem.isDelivered === false && ( <> )} - {item.status === 'delivered' && } + {item.orderItem.isDelivered && }
))} diff --git a/src/components/AsyncComponent.tsx b/src/components/AsyncComponent.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/public/PublicMenuItem.tsx b/src/components/public/PublicMenuItem.tsx index 66ae7c1..181bfc2 100644 --- a/src/components/public/PublicMenuItem.tsx +++ b/src/components/public/PublicMenuItem.tsx @@ -2,12 +2,13 @@ import { useAtom } from 'jotai'; import { PlusIcon, SoupIcon, WineIcon } from 'lucide-react'; -import { orderAtom, type PublicOrderItem } from '~/app/p/[locationSlug]/_state/cart'; +import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; import { Badge } from '~/components/ui/badge'; import { CURRENCIES, type CurrencyId } from '~/domain/currencies'; import { type MenuItem } from '~/domain/menu-items'; import { MenuModeId } from '~/domain/menu-modes'; import { toast } from '~/hooks/use-toast'; +import { getTopPositionedToast } from '~/lib/toast-utils'; export function PublicMenuItem(props: { item: MenuItem; currencyId: CurrencyId; menuMode: MenuModeId }) { const { name, description, price, isNew, type } = props.item; @@ -15,17 +16,24 @@ export function PublicMenuItem(props: { item: MenuItem; currencyId: CurrencyId; const [, setOrder] = useAtom(orderAtom); const addToOrder = () => { - const initialStatus: PublicOrderItem['status'] = 'draft'; setOrder((prevOrder) => { + const { id, name, price, type } = props.item; return { ...prevOrder, - items: [...prevOrder.items, { menuItem: props.item, status: initialStatus }], + items: [ + ...prevOrder.items, + { + menuItem: { id, name, price, type }, + orderItem: { isDelivered: false, isPaid: false }, + }, + ], }; }); toast({ - title: 'Added to cart', - description: `${name} has been added to your order.`, + title: `${name} was added to your cart`, + description: `Press 'Order now!' when you're ready to place your order.`, + className: getTopPositionedToast(), }); }; diff --git a/src/domain/order-items.ts b/src/domain/order-items.ts index a48c7ed..b2913f8 100644 --- a/src/domain/order-items.ts +++ b/src/domain/order-items.ts @@ -1,8 +1,19 @@ -export const PREPAID_STATUSES = ['draft', 'paid'] as const; -export const POSTPAID_STATUSES = ['draft', 'ordered', 'delivered', 'paid'] as const; -export const ORDER_ITEM_STATUSES = [...new Set([...POSTPAID_STATUSES, ...PREPAID_STATUSES])] as const; +// export const PREPAID_STATUSES = ['draft', 'paid'] as const; +// export const POSTPAID_STATUSES = ['draft', 'ordered', 'delivered', 'paid'] as const; +// export const ORDER_ITEM_STATUSES = [...new Set([...POSTPAID_STATUSES, ...PREPAID_STATUSES])] as const; -type PostpaidOrderItemStatus = (typeof POSTPAID_STATUSES)[number]; -type PrepaidOrderItemStatus = (typeof PREPAID_STATUSES)[number]; +import type { InferSelectModel } from 'drizzle-orm'; +import { MenuItem } from '~/domain/menu-items'; +import { orderItems } from '~/server/db/schema'; -export type OrderItemStatus = PostpaidOrderItemStatus | PrepaidOrderItemStatus; +// type PostpaidOrderItemStatus = (typeof POSTPAID_STATUSES)[number]; +// type PrepaidOrderItemStatus = (typeof PREPAID_STATUSES)[number]; + +// export type OrderItemStatus = PostpaidOrderItemStatus | PrepaidOrderItemStatus; + +export type OrderItem = InferSelectModel; + +export interface PublicOrderItem { + menuItem: Pick; + orderItem: { id?: OrderItem['id'] } & Pick; +} diff --git a/src/domain/orders.ts b/src/domain/orders.ts index 409fef2..2239c89 100644 --- a/src/domain/orders.ts +++ b/src/domain/orders.ts @@ -1,14 +1,18 @@ import { type InferSelectModel } from 'drizzle-orm'; import { z } from 'zod'; -import { PublicOrderItem } from '~/app/p/[locationSlug]/_state/cart'; +import { CurrencyId } from '~/domain/currencies'; +import { PublicOrderItem } from '~/domain/order-items'; + import { orders } from '~/server/db/schema'; export const PREPAID_STATUSES = ['draft', 'paid'] as const; export type Order = InferSelectModel; -export type OrderWithItems = Order & { + +export type PublicOrder = z.infer & { currencyId: CurrencyId }; + +export type PublicOrderWithItems = Order & { items: PublicOrderItem[]; - orderId?: string; }; export const orderIdSchema = z.coerce.string(); diff --git a/src/hooks/use-real-time.ts b/src/hooks/use-real-time.ts index 611e80e..296c8ea 100644 --- a/src/hooks/use-real-time.ts +++ b/src/hooks/use-real-time.ts @@ -1,13 +1,14 @@ import { useAtom } from 'jotai'; import { useEffect } from 'react'; -import { orderAtom, type PublicOrder } from '~/app/p/[locationSlug]/_state/cart'; -import { type OrderWithItems } from '~/domain/orders'; +import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; +import { PublicOrder, type PublicOrderWithItems } from '~/domain/orders'; import { useToast } from '~/hooks/use-toast'; import { CHANNELS, EVENTS, pusherClient } from '~/lib/pusher'; +import { getTopPositionedToast } from '~/lib/toast-utils'; -const convertToPublicOrder = (order: OrderWithItems, currencyId: string = 'USD'): PublicOrder => ({ +const convertToPublicOrder = (order: PublicOrderWithItems, currencyId: string = 'USD'): PublicOrder => ({ locationId: order.locationId, - orderId: order.orderId ?? order.id.toString(), + orderId: order.id.toString(), items: order.items, currencyId: currencyId as PublicOrder['currencyId'], }); @@ -24,24 +25,26 @@ export function useRealTimeOrderUpdates(orderId: string | undefined, locationId: const orderChannel = pusherClient.subscribe(CHANNELS.order(orderId)); // Handle new orders in the location - locationChannel.bind(EVENTS.ORDER_CREATED, (data: OrderWithItems) => { + locationChannel.bind(EVENTS.ORDER_CREATED, (data: PublicOrderWithItems) => { toast({ title: 'New Order', description: `Order #${orderId} has been created`, + className: getTopPositionedToast(), }); }); // Handle updates to the current order - orderChannel.bind(EVENTS.ORDER_UPDATED, (data: OrderWithItems) => { + orderChannel.bind(EVENTS.ORDER_UPDATED, (data: PublicOrderWithItems) => { setOrder(convertToPublicOrder(data, order.currencyId)); toast({ title: 'Order Updated', description: `Order #${orderId} has been updated`, + className: getTopPositionedToast(), }); }); // Handle updates to order items - orderChannel.bind(EVENTS.ORDER_ITEM_UPDATED, (data: OrderWithItems) => { + orderChannel.bind(EVENTS.ORDER_ITEM_UPDATED, (data: PublicOrderWithItems) => { setOrder(convertToPublicOrder(data, order.currencyId)); }); @@ -61,18 +64,20 @@ export function useRealTimeLocationUpdates(locationId: number) { const locationChannel = pusherClient.subscribe(CHANNELS.location(locationId)); // Handle new orders in the location - locationChannel.bind(EVENTS.ORDER_CREATED, (data: OrderWithItems) => { + locationChannel.bind(EVENTS.ORDER_CREATED, (data: PublicOrderWithItems) => { toast({ title: 'New Order', - description: `Order #${data.orderId ?? data.id} has been created`, + description: `Order #${data.id} has been created`, + className: getTopPositionedToast(), }); }); // Handle updates to any order in the location - locationChannel.bind(EVENTS.ORDER_UPDATED, (data: OrderWithItems) => { + locationChannel.bind(EVENTS.ORDER_UPDATED, (data: PublicOrderWithItems) => { toast({ title: 'Order Updated', - description: `Order #${data.orderId ?? data.id} has been updated`, + description: `Order #${data.id} has been updated`, + className: getTopPositionedToast(), }); }); diff --git a/src/lib/toast-utils.ts b/src/lib/toast-utils.ts new file mode 100644 index 0000000..890a951 --- /dev/null +++ b/src/lib/toast-utils.ts @@ -0,0 +1,5 @@ +import { cn } from '~/lib/utils'; + +export function getTopPositionedToast() { + return cn('left-2 top-2 right-2 flex fixed md:max-w-[420px] md:left-4 md:top-4 md:right-4'); +} diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts index 38ae974..11febfe 100644 --- a/src/server/db/schema.ts +++ b/src/server/db/schema.ts @@ -4,7 +4,6 @@ import { relations, sql } from 'drizzle-orm'; import { boolean, decimal, index, integer, pgTableCreator, primaryKey, timestamp, varchar } from 'drizzle-orm/pg-core'; import { MENU_MODES, MenuModeId } from '~/domain/menu-modes'; -import { ORDER_ITEM_STATUSES, OrderItemStatus } from '~/domain/order-items'; import { CURRENCIES, CurrencyId } from '../../domain/currencies'; /** @@ -150,30 +149,21 @@ export const orders = createTable('order', { updatedAt: timestamp('updated_at', { withTimezone: true }).$onUpdate(() => new Date()), }); -const defaultOrderItemStatus: OrderItemStatus = 'draft'; - -export const orderItems = createTable( - 'order_item', - { - id: integer('id').primaryKey().generatedByDefaultAsIdentity(), - orderId: integer('order_id') - .notNull() - .references(() => orders.id), - menuItemId: integer('menu_item_id') - .notNull() - .references(() => menuItems.id), - status: varchar('status', { length: 10 }).notNull().default(defaultOrderItemStatus), - createdAt: timestamp('created_at', { withTimezone: true }) - .default(sql`CURRENT_TIMESTAMP`) - .notNull(), - updatedAt: timestamp('updated_at', { withTimezone: true }).$onUpdate(() => new Date()), - }, - () => [ - { - statusCheck: sql`CHECK (status IN (${sql.join(Array.from(ORDER_ITEM_STATUSES))}))`, - }, - ], -); +export const orderItems = createTable('order_item', { + id: integer('id').primaryKey().generatedByDefaultAsIdentity(), + orderId: integer('order_id') + .notNull() + .references(() => orders.id), + menuItemId: integer('menu_item_id') + .notNull() + .references(() => menuItems.id), + isDelivered: boolean('is_delivered').default(false).notNull(), + isPaid: boolean('is_paid').default(false).notNull(), + createdAt: timestamp('created_at', { withTimezone: true }) + .default(sql`CURRENT_TIMESTAMP`) + .notNull(), + updatedAt: timestamp('updated_at', { withTimezone: true }).$onUpdate(() => new Date()), +}); // optional: add relations for `.with` export const ordersRelations = relations(orders, ({ many }) => ({ diff --git a/src/server/queries/orders.ts b/src/server/queries/orders.ts index 7d19649..38be5e8 100644 --- a/src/server/queries/orders.ts +++ b/src/server/queries/orders.ts @@ -1,8 +1,8 @@ -import { sql, type InferSelectModel } from 'drizzle-orm'; +import { sql } from 'drizzle-orm'; import { z } from 'zod'; import type { LocationId } from '~/domain/locations'; -import { OrderItemStatus } from '~/domain/order-items'; -import { orderFormSchema } from '~/domain/orders'; +import { PublicOrderItem } from '~/domain/order-items'; +import { orderFormSchema, OrderId, PublicOrderWithItems } from '~/domain/orders'; import { AppError } from '~/lib/error-utils.server'; import { db } from '~/server/db'; import { orderItems, orders } from '~/server/db/schema'; @@ -14,7 +14,7 @@ import { getLocationForCurrentUserOrThrow } from '~/server/queries/locations'; // return `ORD-${timestamp}${randomStr}`; // } -export async function createOrder(data: z.infer) { +export async function createOrder(data: z.infer): Promise { return await db.transaction(async (tx) => { const [order] = await tx .insert(orders) @@ -29,36 +29,122 @@ export async function createOrder(data: z.infer) { throw new AppError({ internalMessage: 'Could not insert order' }); } + // if (data.items) { + // for (let i = 0; i < data.items.length; i++) { + // const item = data.items[i]; + // await tx.insert(orderItems).values({ + // orderId: order.id, + // menuItemId: item!.menuItem.id, + // isDelivered: false, + // isPaid: false, + // createdAt: sql`CURRENT_TIMESTAMP`, + // updatedAt: sql`CURRENT_TIMESTAMP`, + // }); + // } + // } + const insertedItems: PublicOrderItem[] = []; if (data.items) { for (let i = 0; i < data.items.length; i++) { const item = data.items[i]; - const status: OrderItemStatus = 'ordered'; - await tx.insert(orderItems).values({ - orderId: order.id, - menuItemId: item!.menuItem.id, - status, - createdAt: sql`CURRENT_TIMESTAMP`, - updatedAt: sql`CURRENT_TIMESTAMP`, - }); + const [insertedItem] = await tx + .insert(orderItems) + .values({ + orderId: order.id, + menuItemId: item!.menuItem.id, + isDelivered: false, + isPaid: false, + createdAt: sql`CURRENT_TIMESTAMP`, + updatedAt: sql`CURRENT_TIMESTAMP`, + }) + .returning(); + if (insertedItem) { + const insertedPublicItem: PublicOrderItem = { + menuItem: { + id: item!.menuItem.id, + name: item!.menuItem.name, + price: item!.menuItem.price, + }, + orderItem: { + id: insertedItem.id, + isDelivered: false, + isPaid: false, + }, + }; + insertedItems.push(insertedPublicItem); + } } } - - return order; + const orderWithItems: PublicOrderWithItems = { + ...order, + items: insertedItems, + }; + return orderWithItems; }); } -type OrderWithItems = InferSelectModel & { - orderItems: Array>; -}; - -export async function getOpenOrdersByLocation(locationId: LocationId): Promise { +export async function getOpenOrdersByLocation(locationId: LocationId): Promise { const validLocation = await getLocationForCurrentUserOrThrow(locationId); const items = await db.query.orders.findMany({ where: (orders, { eq }) => eq(orders.locationId, validLocation.id), with: { - orderItems: true, // fetch all columns of each orderItem + orderItems: true, }, }); - return items; + const ordersWithItems: PublicOrderWithItems[] = items.map((order) => ({ + id: order.id, + locationId: order.locationId, + createdAt: order.createdAt, + updatedAt: order.updatedAt, + items: order.orderItems.map((orderItem) => ({ + menuItem: { + id: orderItem.menuItemId, + // You may need to fetch menuItem name and price here if not included in orderItem + name: '', // TODO Placeholder, replace with actual value if available + price: '0', // TODO Placeholder, replace with actual value if available + }, + orderItem: { + id: orderItem.id, + isDelivered: orderItem.isDelivered, + isPaid: orderItem.isPaid, + }, + })), + })); + return ordersWithItems; +} + +export async function getOrderById(locationId: LocationId, orderId: OrderId): Promise { + const validLocation = await getLocationForCurrentUserOrThrow(locationId); + + const order = await db.query.orders.findFirst({ + where: (orders, { and, eq }) => and(eq(orders.locationId, validLocation.id), eq(orders.id, Number(orderId))), + with: { + orderItems: true, + }, + }); + + if (!order) { + throw new AppError({ internalMessage: 'Order not found' }); + } + + const orderWithItems: PublicOrderWithItems = { + id: order.id, + locationId: order.locationId, + createdAt: order.createdAt, + updatedAt: order.updatedAt, + items: order.orderItems.map((orderItem) => ({ + menuItem: { + id: orderItem.menuItemId, + // You may need to fetch menuItem name and price here if not included in orderItem + name: '', // TODO Placeholder, replace with actual value if available + price: '0', // TODO Placeholder, replace with actual value if available + }, + orderItem: { + id: orderItem.id, + isDelivered: orderItem.isDelivered, + isPaid: orderItem.isPaid, + }, + })), + }; + return orderWithItems; } From ecbcc575ed1cde85ee09534a6ea291e86a2d653e Mon Sep 17 00:00:00 2001 From: dopoto Date: Mon, 19 May 2025 10:35:42 +0300 Subject: [PATCH 29/47] sort --- package.json | 35 +- pnpm-lock.yaml | 1449 ++++++----------- .../markOrderItemAsDeliveredAction.ts | 3 +- .../live/_components/OrderCard.tsx | 4 +- src/server/db/schema.ts | 1 - src/server/queries/orders.ts | 31 +- 6 files changed, 558 insertions(+), 965 deletions(-) diff --git a/package.json b/package.json index 28a239c..215961f 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,8 @@ "knip": "knip" }, "dependencies": { - "@clerk/nextjs": "^6.19.3", - "@clerk/themes": "2.2.44", + "@clerk/nextjs": "^6.19.4", + "@clerk/themes": "2.2.45", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", @@ -66,18 +66,18 @@ "embla-carousel-react": "^8.6.0", "geist": "^1.4.2", "jotai": "^2.12.4", - "lucide-react": "^0.510.0", + "lucide-react": "^0.511.0", "next": "15.3.2", "next-themes": "^0.4.6", "postgres": "^3.4.5", - "posthog-js": "^1.242.1", + "posthog-js": "^1.242.3", "posthog-node": "^4.17.1", "pusher": "^5.2.0", "pusher-js": "^8.4.0", "react": "^19.1.0", "react-confetti": "6.4.0", "react-dom": "^19.1.0", - "react-hook-form": "^7.56.3", + "react-hook-form": "^7.56.4", "react-loading-skeleton": "^3.5.0", "react-qr-code": "^2.0.15", "react-select": "^5.10.1", @@ -93,24 +93,24 @@ "devDependencies": { "@chromatic-com/storybook": "3.2.6", "@eslint/eslintrc": "^3.3.1", - "@storybook/addon-essentials": "8.6.12", - "@storybook/addon-onboarding": "8.6.12", - "@storybook/blocks": "8.6.12", - "@storybook/nextjs": "8.6.12", - "@storybook/react": "8.6.12", - "@storybook/test": "8.6.12", - "@tailwindcss/postcss": "^4.1.6", + "@storybook/addon-essentials": "8.6.14", + "@storybook/addon-onboarding": "8.6.14", + "@storybook/blocks": "8.6.14", + "@storybook/nextjs": "8.6.14", + "@storybook/react": "8.6.14", + "@storybook/test": "8.6.14", + "@tailwindcss/postcss": "^4.1.7", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.3.0", "@types/eslint": "^9.6.1", "@types/jest": "^29.5.14", - "@types/node": "^22.15.18", + "@types/node": "^22.15.19", "@types/react": "^19.1.4", "@types/react-dom": "^19.1.5", "cross-env": "^7.0.3", "drizzle-kit": "^0.31.1", - "eslint": "9.26.0", + "eslint": "9.27.0", "eslint-config-next": "^15.3.2", "eslint-config-prettier": "^10.1.5", "eslint-plugin-drizzle": "^0.2.3", @@ -123,9 +123,9 @@ "prettier": "^3.5.3", "prettier-plugin-organize-imports": "4.1.0", "prettier-plugin-tailwindcss": "^0.6.11", - "storybook": "8.6.12", - "tailwindcss": "^4.1.6", - "ts-jest": "^29.3.3", + "storybook": "8.6.14", + "tailwindcss": "^4.1.7", + "ts-jest": "^29.3.4", "ts-node": "^10.9.2", "typescript": "^5.8.3", "typescript-eslint": "^8.32.1" @@ -141,6 +141,7 @@ "onlyBuiltDependencies": [ "@clerk/shared", "@sentry/cli", + "@tailwindcss/oxide", "bufferutil", "core-js-pure", "esbuild", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bf22868..9bb8c49 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,11 +9,11 @@ importers: .: dependencies: '@clerk/nextjs': - specifier: ^6.19.3 - version: 6.19.3(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + specifier: ^6.19.4 + version: 6.19.4(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@clerk/themes': - specifier: 2.2.44 - version: 2.2.44 + specifier: 2.2.45 + version: 2.2.45 '@dnd-kit/core': specifier: ^6.3.1 version: 6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -25,7 +25,7 @@ importers: version: 3.2.2(react@19.1.0) '@hookform/resolvers': specifier: ^5.0.1 - version: 5.0.1(react-hook-form@7.56.3(react@19.1.0)) + version: 5.0.1(react-hook-form@7.56.4(react@19.1.0)) '@next/env': specifier: ~15.3.2 version: 15.3.2 @@ -126,8 +126,8 @@ importers: specifier: ^2.12.4 version: 2.12.4(@types/react@19.1.4)(react@19.1.0) lucide-react: - specifier: ^0.510.0 - version: 0.510.0(react@19.1.0) + specifier: ^0.511.0 + version: 0.511.0(react@19.1.0) next: specifier: 15.3.2 version: 15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -138,8 +138,8 @@ importers: specifier: ^3.4.5 version: 3.4.5 posthog-js: - specifier: ^1.242.1 - version: 1.242.1 + specifier: ^1.242.3 + version: 1.242.3 posthog-node: specifier: ^4.17.1 version: 4.17.1 @@ -159,8 +159,8 @@ importers: specifier: ^19.1.0 version: 19.1.0(react@19.1.0) react-hook-form: - specifier: ^7.56.3 - version: 7.56.3(react@19.1.0) + specifier: ^7.56.4 + version: 7.56.4(react@19.1.0) react-loading-skeleton: specifier: ^3.5.0 version: 3.5.0(react@19.1.0) @@ -178,13 +178,13 @@ importers: version: 0.0.1 stripe: specifier: ^18.1.0 - version: 18.1.0(@types/node@22.15.18) + version: 18.1.0(@types/node@22.15.19) tailwind-merge: specifier: ^3.3.0 version: 3.3.0 tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@4.1.6) + version: 1.0.7(tailwindcss@4.1.7) truncate-middle: specifier: ^2.0.1 version: 2.0.1 @@ -197,31 +197,31 @@ importers: devDependencies: '@chromatic-com/storybook': specifier: 3.2.6 - version: 3.2.6(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + version: 3.2.6(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) '@eslint/eslintrc': specifier: ^3.3.1 version: 3.3.1 '@storybook/addon-essentials': - specifier: 8.6.12 - version: 8.6.12(@types/react@19.1.4)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + specifier: 8.6.14 + version: 8.6.14(@types/react@19.1.4)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) '@storybook/addon-onboarding': - specifier: 8.6.12 - version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + specifier: 8.6.14 + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) '@storybook/blocks': - specifier: 8.6.12 - version: 8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + specifier: 8.6.14 + version: 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) '@storybook/nextjs': - specifier: 8.6.12 - version: 8.6.12(babel-plugin-macros@3.1.0)(esbuild@0.25.2)(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(type-fest@4.41.0)(typescript@5.8.3)(webpack-hot-middleware@2.26.1)(webpack@5.98.0(esbuild@0.25.2)) + specifier: 8.6.14 + version: 8.6.14(babel-plugin-macros@3.1.0)(esbuild@0.25.2)(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))(type-fest@4.41.0)(typescript@5.8.3)(webpack-hot-middleware@2.26.1)(webpack@5.98.0(esbuild@0.25.2)) '@storybook/react': - specifier: 8.6.12 - version: 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3) + specifier: 8.6.14 + version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3) '@storybook/test': - specifier: 8.6.12 - version: 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + specifier: 8.6.14 + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) '@tailwindcss/postcss': - specifier: ^4.1.6 - version: 4.1.6 + specifier: ^4.1.7 + version: 4.1.7 '@testing-library/dom': specifier: ^10.4.0 version: 10.4.0 @@ -238,8 +238,8 @@ importers: specifier: ^29.5.14 version: 29.5.14 '@types/node': - specifier: ^22.15.18 - version: 22.15.18 + specifier: ^22.15.19 + version: 22.15.19 '@types/react': specifier: ^19.1.4 version: 19.1.4 @@ -253,32 +253,32 @@ importers: specifier: ^0.31.1 version: 0.31.1 eslint: - specifier: 9.26.0 - version: 9.26.0(jiti@2.4.2) + specifier: 9.27.0 + version: 9.27.0(jiti@2.4.2) eslint-config-next: specifier: ^15.3.2 - version: 15.3.2(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + version: 15.3.2(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) eslint-config-prettier: specifier: ^10.1.5 - version: 10.1.5(eslint@9.26.0(jiti@2.4.2)) + version: 10.1.5(eslint@9.27.0(jiti@2.4.2)) eslint-plugin-drizzle: specifier: ^0.2.3 - version: 0.2.3(eslint@9.26.0(jiti@2.4.2)) + version: 0.2.3(eslint@9.27.0(jiti@2.4.2)) eslint-plugin-storybook: specifier: ^0.12.0 - version: 0.12.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + version: 0.12.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) + version: 29.7.0(@types/node@22.15.19)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3)) jest-environment-jsdom: specifier: ^29.7.0 version: 29.7.0(bufferutil@4.0.9) jotai-devtools: specifier: ^0.12.0 - version: 0.12.0(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(redux@5.0.1)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + version: 0.12.0(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(redux@5.0.1)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) knip: specifier: ^5.56.0 - version: 5.56.0(@types/node@22.15.18)(typescript@5.8.3) + version: 5.56.0(@types/node@22.15.19)(typescript@5.8.3) postcss: specifier: ^8.5.3 version: 8.5.3 @@ -292,23 +292,23 @@ importers: specifier: ^0.6.11 version: 0.6.11(prettier-plugin-organize-imports@4.1.0(prettier@3.5.3)(typescript@5.8.3))(prettier@3.5.3) storybook: - specifier: 8.6.12 - version: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + specifier: 8.6.14 + version: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) tailwindcss: - specifier: ^4.1.6 - version: 4.1.6 + specifier: ^4.1.7 + version: 4.1.7 ts-jest: - specifier: ^29.3.3 - version: 29.3.3(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.9))(esbuild@0.25.2)(jest@29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)))(typescript@5.8.3) + specifier: ^29.3.4 + version: 29.3.4(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.9))(esbuild@0.25.2)(jest@29.7.0(@types/node@22.15.19)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3)))(typescript@5.8.3) ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@22.15.18)(typescript@5.8.3) + version: 10.9.2(@types/node@22.15.19)(typescript@5.8.3) typescript: specifier: ^5.8.3 version: 5.8.3 typescript-eslint: specifier: ^8.32.1 - version: 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + version: 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) packages: @@ -961,8 +961,8 @@ packages: peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@clerk/backend@1.32.1': - resolution: {integrity: sha512-Y7Txq7L3JPilb4kreHpp+3z4HUta+A2LdcurayoUEQyIzBH93AXnWnuRX1EFgSaQ6THsUq5IwmhemETLitNdKg==} + '@clerk/backend@1.32.2': + resolution: {integrity: sha512-upJ9/EnHTQbG8YcdYYdxjumZnmSADU4Zbeag1m37fk0h24MwRP3iwAwk/5MoL2tB4cKTHhSBUDTlGpejSQVCPw==} engines: {node: '>=18.17.0'} peerDependencies: svix: ^1.62.0 @@ -970,23 +970,23 @@ packages: svix: optional: true - '@clerk/clerk-react@5.31.3': - resolution: {integrity: sha512-EzswkUMsWVrLbnE12+gD7W05jTPpfnHVhC7TjWHaZphLxhY+8Tt8xHfg5Rhj52/d4vV+BXE12o6u1lFo8mls7w==} + '@clerk/clerk-react@5.31.4': + resolution: {integrity: sha512-VtjOEzq/ncwHRn23xhmy4DRefrrSeUkKHiB/EighusYVkjmpzWMXYGD9Wdd79hwUhJesUsBiQdZhE5qkJ+mnJA==} engines: {node: '>=18.17.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 - '@clerk/nextjs@6.19.3': - resolution: {integrity: sha512-bpwzbUajkFqGGmhRn95Pp/1NoDvqniZKSbVbXHHdzDqAB9QjLAjoHLgyehEdAn/2nkiIjqk2PKH3Kf3CiXsH4Q==} + '@clerk/nextjs@6.19.4': + resolution: {integrity: sha512-UlGwjJNY2+xkCwGhBbLdUfwgV3te3+Q3StaeueVQ3/+QrGjfyuHvMftH9lDgmTUExQ/qtWESycrH1kKVFNXsHg==} engines: {node: '>=18.17.0'} peerDependencies: next: ^13.5.7 || ^14.2.25 || ^15.2.3 react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 - '@clerk/shared@3.9.0': - resolution: {integrity: sha512-M+2IwE8DUof7RAjo0iCNlr4pRuXo5g8rDvk7Q1L9xVFSh7N9aA87BX1ctmUbus/BKrCSDLRiYQ6V0uGATg9iNQ==} + '@clerk/shared@3.9.1': + resolution: {integrity: sha512-Gw7yPaas3lv+pkkbBwuqVVtWVH1nZl1hF8kVvdEhPALOkf6ww6azL6qcHaiFUHCW+iult3flJjplduFrfWF50g==} engines: {node: '>=18.17.0'} peerDependencies: react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 @@ -997,12 +997,12 @@ packages: react-dom: optional: true - '@clerk/themes@2.2.44': - resolution: {integrity: sha512-jKVICOjaJOm7uE3g/K/wRWJ3MY9iBDZ6ua5vO+qnU1poTohKC7QJYX4tY9vmAJ8B/K/G3PGXnhXzA1dEF66ERQ==} + '@clerk/themes@2.2.45': + resolution: {integrity: sha512-tm6eWoyap0oszBP6IQMDgVirZeaEebjAnCWBiEsgnYk0G8iY52imK5JLtmTlSuhDGBw1xNKmOzP850AZgOtDhA==} engines: {node: '>=18.17.0'} - '@clerk/types@4.58.1': - resolution: {integrity: sha512-bs1jQF4Nxw4a34q8TZEqY2Jyuj8kcMgYHUDCP060XkdvKWUqbQZW7Hb3YBrxTqLFLawku9cWpgP3/41Os1584g==} + '@clerk/types@4.59.0': + resolution: {integrity: sha512-VZ61lDWoz9cWTlSpO1KMGq7utl96ZuSBIOpM6togxYTp+TG0kD6QEJVinMaJLREtx8jRvXpMG7ZzBLE3zy0GSA==} engines: {node: '>=18.17.0'} '@cspotcode/source-map-support@0.8.1': @@ -1037,9 +1037,6 @@ packages: '@emnapi/core@1.4.3': resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} - '@emnapi/runtime@1.3.1': - resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} - '@emnapi/runtime@1.4.0': resolution: {integrity: sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==} @@ -1401,24 +1398,24 @@ packages: resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.13.0': - resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==} + '@eslint/core@0.14.0': + resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.26.0': - resolution: {integrity: sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==} + '@eslint/js@9.27.0': + resolution: {integrity: sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.2.8': - resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==} + '@eslint/plugin-kit@0.3.1': + resolution: {integrity: sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@fastify/otel@https://codeload.github.com/getsentry/fastify-otel/tar.gz/ae3088d65e286bdc94ac5d722573537d6a6671bb': @@ -1816,10 +1813,6 @@ packages: '@types/react': '>=16' react: '>=16' - '@modelcontextprotocol/sdk@1.11.0': - resolution: {integrity: sha512-k/1pb70eD638anoi0e8wUGAlbMJXyvdV4p62Ko+EZ7eBe1xMx8Uhak1R5DgfoofsK5IBBnRwsYGTaLZl+6/+RQ==} - engines: {node: '>=18'} - '@napi-rs/wasm-runtime@0.2.9': resolution: {integrity: sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==} @@ -2906,104 +2899,104 @@ packages: '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} - '@storybook/addon-actions@8.6.12': - resolution: {integrity: sha512-B5kfiRvi35oJ0NIo53CGH66H471A3XTzrfaa6SxXEJsgxxSeKScG5YeXcCvLiZfvANRQ7QDsmzPUgg0o3hdMXw==} + '@storybook/addon-actions@8.6.14': + resolution: {integrity: sha512-mDQxylxGGCQSK7tJPkD144J8jWh9IU9ziJMHfB84PKpI/V5ZgqMDnpr2bssTrUaGDqU5e1/z8KcRF+Melhs9pQ==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 - '@storybook/addon-backgrounds@8.6.12': - resolution: {integrity: sha512-lmIAma9BiiCTbJ8YfdZkXjpnAIrOUcgboLkt1f6XJ78vNEMnLNzD9gnh7Tssz1qrqvm34v9daDjIb+ggdiKp3Q==} + '@storybook/addon-backgrounds@8.6.14': + resolution: {integrity: sha512-l9xS8qWe5n4tvMwth09QxH2PmJbCctEvBAc1tjjRasAfrd69f7/uFK4WhwJAstzBTNgTc8VXI4w8ZR97i1sFbg==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 - '@storybook/addon-controls@8.6.12': - resolution: {integrity: sha512-9VSRPJWQVb9wLp21uvpxDGNctYptyUX0gbvxIWOHMH3R2DslSoq41lsC/oQ4l4zSHVdL+nq8sCTkhBxIsjKqdQ==} + '@storybook/addon-controls@8.6.14': + resolution: {integrity: sha512-IiQpkNJdiRyA4Mq9mzjZlvQugL/aE7hNgVxBBGPiIZG6wb6Ht9hNnBYpap5ZXXFKV9p2qVI0FZK445ONmAa+Cw==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 - '@storybook/addon-docs@8.6.12': - resolution: {integrity: sha512-kEezQjAf/p3SpDzLABgg4fbT48B6dkT2LiZCKTRmCrJVtuReaAr4R9MMM6Jsph6XjbIj/SvOWf3CMeOPXOs9sg==} + '@storybook/addon-docs@8.6.14': + resolution: {integrity: sha512-Obpd0OhAF99JyU5pp5ci17YmpcQtMNgqW2pTXV8jAiiipWpwO++hNDeQmLmlSXB399XjtRDOcDVkoc7rc6JzdQ==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 - '@storybook/addon-essentials@8.6.12': - resolution: {integrity: sha512-Y/7e8KFlttaNfv7q2zoHMPdX6hPXHdsuQMAjYl5NG9HOAJREu4XBy4KZpbcozRe4ApZ78rYsN/MO1EuA+bNMIA==} + '@storybook/addon-essentials@8.6.14': + resolution: {integrity: sha512-5ZZSHNaW9mXMOFkoPyc3QkoNGdJHETZydI62/OASR0lmPlJ1065TNigEo5dJddmZNn0/3bkE8eKMAzLnO5eIdA==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 - '@storybook/addon-highlight@8.6.12': - resolution: {integrity: sha512-9FITVxdoycZ+eXuAZL9ElWyML/0fPPn9UgnnAkrU7zkMi+Segq/Tx7y+WWanC5zfWZrXAuG6WTOYEXeWQdm//w==} + '@storybook/addon-highlight@8.6.14': + resolution: {integrity: sha512-4H19OJlapkofiE9tM6K/vsepf4ir9jMm9T+zw5L85blJZxhKZIbJ6FO0TCG9PDc4iPt3L6+aq5B0X29s9zicNQ==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 - '@storybook/addon-measure@8.6.12': - resolution: {integrity: sha512-tACmwqqOvutaQSduw8SMb62wICaT1rWaHtMN3vtWXuxgDPSdJQxLP+wdVyRYMAgpxhLyIO7YRf++Hfha9RHgFg==} + '@storybook/addon-measure@8.6.14': + resolution: {integrity: sha512-1Tlyb72NX8aAqm6I6OICsUuGOP6hgnXcuFlXucyhKomPa6j3Eu2vKu561t/f0oGtAK2nO93Z70kVaEh5X+vaGw==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 - '@storybook/addon-onboarding@8.6.12': - resolution: {integrity: sha512-/cgxaLy6tr6xO0+QO+qV5rPZS5/c15Daywvg/F03lifLGkMuyn/JDuhu0J5i1LbFsL1RYdf4sjrTOmLXbOT6+Q==} + '@storybook/addon-onboarding@8.6.14': + resolution: {integrity: sha512-bHdHiGJFigVcSzMIsNLHY5IODZHr+nKwyz5/QOZLMkLcGH2IaUbOJfm4RyGOaTTPsUtAKbdsVXNEG3Otf+qO9A==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 - '@storybook/addon-outline@8.6.12': - resolution: {integrity: sha512-1ylwm+n1s40S91No0v9T4tCjZORu3GbnjINlyjYTDLLhQHyBQd3nWR1Y1eewU4xH4cW9SnSLcMQFS/82xHqU6A==} + '@storybook/addon-outline@8.6.14': + resolution: {integrity: sha512-CW857JvN6OxGWElqjlzJO2S69DHf+xO3WsEfT5mT3ZtIjmsvRDukdWfDU9bIYUFyA2lFvYjncBGjbK+I91XR7w==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 - '@storybook/addon-toolbars@8.6.12': - resolution: {integrity: sha512-HEcSzo1DyFtIu5/ikVOmh5h85C1IvK9iFKSzBR6ice33zBOaehVJK+Z5f487MOXxPsZ63uvWUytwPyViGInj+g==} + '@storybook/addon-toolbars@8.6.14': + resolution: {integrity: sha512-W/wEXT8h3VyZTVfWK/84BAcjAxTdtRiAkT2KAN0nbSHxxB5KEM1MjKpKu2upyzzMa3EywITqbfy4dP6lpkVTwQ==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 - '@storybook/addon-viewport@8.6.12': - resolution: {integrity: sha512-EXK2LArAnABsPP0leJKy78L/lbMWow+EIJfytEP5fHaW4EhMR6h7Hzaqzre6U0IMMr/jVFa1ci+m0PJ0eQc2bw==} + '@storybook/addon-viewport@8.6.14': + resolution: {integrity: sha512-gNzVQbMqRC+/4uQTPI2ZrWuRHGquTMZpdgB9DrD88VTEjNudP+J6r8myLfr2VvGksBbUMHkGHMXHuIhrBEnXYA==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 - '@storybook/blocks@8.6.12': - resolution: {integrity: sha512-DohlTq6HM1jDbHYiXL4ZvZ00VkhpUp5uftzj/CZDLY1fYHRjqtaTwWm2/OpceivMA8zDitLcq5atEZN+f+siTg==} + '@storybook/blocks@8.6.14': + resolution: {integrity: sha512-rBMHAfA39AGHgkrDze4RmsnQTMw1ND5fGWobr9pDcJdnDKWQWNRD7Nrlxj0gFlN3n4D9lEZhWGdFrCbku7FVAQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^8.6.12 + storybook: ^8.6.14 peerDependenciesMeta: react: optional: true react-dom: optional: true - '@storybook/builder-webpack5@8.6.12': - resolution: {integrity: sha512-Z7RsQ/1+HbxdbM69JrEFcTL+pnVKUTMmeURMn5/eOvYTGjBtM18vbQTj0LjCUDIjC+v9U+uX8ZJEUVxFbGcxBw==} + '@storybook/builder-webpack5@8.6.14': + resolution: {integrity: sha512-YZYAqc6NBKoMTKZpjxnkMch6zDtMkBZdS/yaji1+wJX2QPFBwTbSh7SpeBxDp1S11gXSAJ4f1btUWeqSqo8nJA==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 typescript: '*' peerDependenciesMeta: typescript: optional: true - '@storybook/components@8.6.12': - resolution: {integrity: sha512-FiaE8xvCdvKC2arYusgtlDNZ77b8ysr8njAYQZwwaIHjy27TbR2tEpLDCmUwSbANNmivtc/xGEiDDwcNppMWlQ==} + '@storybook/components@8.6.14': + resolution: {integrity: sha512-HNR2mC5I4Z5ek8kTrVZlIY/B8gJGs5b3XdZPBPBopTIN6U/YHXiDyOjY3JlaS4fSG1fVhp/Qp1TpMn1w/9m1pw==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/core-webpack@8.6.12': - resolution: {integrity: sha512-TiE+KOm0hxb/p0JxeGHKxqTNX+xnTOFsBh6uloCSuvodutJ5pR/XpxKVxwo1gtSc0Uq3qpgbMhW6qYlYQetnKA==} + '@storybook/core-webpack@8.6.14': + resolution: {integrity: sha512-iG7r8osNKabSGBbuJuSeMWKbU+ilt5PvzTYkClcYaagla/DliXkXvfywA6jOugVk/Cpx+c6tVKlPfjLcaQHwmw==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 - '@storybook/core@8.6.12': - resolution: {integrity: sha512-t+ZuDzAlsXKa6tLxNZT81gEAt4GNwsKP/Id2wluhmUWD/lwYW0uum1JiPUuanw8xD6TdakCW/7ULZc7aQUBLCQ==} + '@storybook/core@8.6.14': + resolution: {integrity: sha512-1P/w4FSNRqP8j3JQBOi3yGt8PVOgSRbP66Ok520T78eJBeqx9ukCfl912PQZ7SPbW3TIunBwLXMZOjZwBB/JmA==} peerDependencies: prettier: ^2 || ^3 peerDependenciesMeta: prettier: optional: true - '@storybook/csf-plugin@8.6.12': - resolution: {integrity: sha512-6s8CnP1aoKPb3XtC0jRLUp8M5vTA8RhGAwQDKUsFpCC7g89JR9CaKs9FY2ZSzsNbjR15uASi7b3K8BzeYumYQg==} + '@storybook/csf-plugin@8.6.14': + resolution: {integrity: sha512-dErtc9teAuN+eelN8FojzFE635xlq9cNGGGEu0WEmMUQ4iJ8pingvBO1N8X3scz4Ry7KnxX++NNf3J3gpxS8qQ==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 '@storybook/csf@0.1.13': resolution: {integrity: sha512-7xOOwCLGB3ebM87eemep89MYRFTko+D8qE7EdAAq74lgdqRR5cOUtYWJLjO2dLtP94nqoOdHJo6MdLLKzg412Q==} @@ -3018,24 +3011,24 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - '@storybook/instrumenter@8.6.12': - resolution: {integrity: sha512-VK5fYAF8jMwWP/u3YsmSwKGh+FeSY8WZn78flzRUwirp2Eg1WWjsqPRubAk7yTpcqcC/km9YMF3KbqfzRv2s/A==} + '@storybook/instrumenter@8.6.14': + resolution: {integrity: sha512-iG4MlWCcz1L7Yu8AwgsnfVAmMbvyRSk700Mfy2g4c8y5O+Cv1ejshE1LBBsCwHgkuqU0H4R0qu4g23+6UnUemQ==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 - '@storybook/manager-api@8.6.12': - resolution: {integrity: sha512-O0SpISeJLNTQvhSBOsWzzkCgs8vCjOq1578rwqHlC6jWWm4QmtfdyXqnv7rR1Hk08kQ+Dzqh0uhwHx0nfwy4nQ==} + '@storybook/manager-api@8.6.14': + resolution: {integrity: sha512-ez0Zihuy17udLbfHZQXkGqwtep0mSGgHcNzGN7iZrMP1m+VmNo+7aGCJJdvXi7+iU3yq8weXSQFWg5DqWgLS7g==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/nextjs@8.6.12': - resolution: {integrity: sha512-I9y5xpOOSCo91IK4jf8nT/V9R0sPiJblKc0gJg0sOSSNgsswGxJq9anah6PEbYhlZtbptmhGOL6IeUR+pI6Qzw==} + '@storybook/nextjs@8.6.14': + resolution: {integrity: sha512-HbOOpwxJxO8nIDBvEQL3Pt51GHxnSeVxQ/WApr1HCT5Ffu6KCHz8WVsX56taHdigxjonSq0NTnog+aTIP06Nkw==} engines: {node: '>=18.0.0'} peerDependencies: next: ^13.5.0 || ^14.0.0 || ^15.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.6.12 + storybook: ^8.6.14 typescript: '*' webpack: ^5.0.0 peerDependenciesMeta: @@ -3044,20 +3037,20 @@ packages: webpack: optional: true - '@storybook/preset-react-webpack@8.6.12': - resolution: {integrity: sha512-aCCHjR/jsVPVThRH7nK70wS0Od44M6hqkkakg3xr7LETZZGj99heen6t4VHvz8gcQYT9l6R/oZwCl7f/PQ3ZBQ==} + '@storybook/preset-react-webpack@8.6.14': + resolution: {integrity: sha512-M7Q6ErNx7N2hQorTz0OLa3YV8nc8OcvkDlCxqqnkHPGQNEIWEpeDvq3wn2OvZlrHDpchyuiquGXZ8aztVtBP2g==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.6.12 + storybook: ^8.6.14 typescript: '*' peerDependenciesMeta: typescript: optional: true - '@storybook/preview-api@8.6.12': - resolution: {integrity: sha512-84FE3Hrs0AYKHqpDZOwx1S/ffOfxBdL65lhCoeI8GoWwCkzwa9zEP3kvXBo/BnEDO7nAfxvMhjASTZXbKRJh5Q==} + '@storybook/preview-api@8.6.14': + resolution: {integrity: sha512-2GhcCd4dNMrnD7eooEfvbfL4I83qAqEyO0CO7JQAmIO6Rxb9BsOLLI/GD5HkvQB73ArTJ+PT50rfaO820IExOQ==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 @@ -3067,21 +3060,21 @@ packages: typescript: '>= 4.x' webpack: '>= 4' - '@storybook/react-dom-shim@8.6.12': - resolution: {integrity: sha512-51QvoimkBzYs8s3rCYnY5h0cFqLz/Mh0vRcughwYaXckWzDBV8l67WBO5Xf5nBsukCbWyqBVPpEQLww8s7mrLA==} + '@storybook/react-dom-shim@8.6.14': + resolution: {integrity: sha512-0hixr3dOy3f3M+HBofp3jtMQMS+sqzjKNgl7Arfuj3fvjmyXOks/yGjDImySR4imPtEllvPZfhiQNlejheaInw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.6.12 + storybook: ^8.6.14 - '@storybook/react@8.6.12': - resolution: {integrity: sha512-NzxlHLA5DkDgZM/dMwTYinuzRs6rsUPmlqP+NIv6YaciQ4NGnTYyOC7R/SqI6HHFm8ZZ5eMYvpfiFmhZ9rU+rQ==} + '@storybook/react@8.6.14': + resolution: {integrity: sha512-BOepx5bBFwl/CPI+F+LnmMmsG1wQYmrX/UQXgUbHQUU9Tj7E2ndTnNbpIuSLc8IrM03ru+DfwSg1Co3cxWtT+g==} engines: {node: '>=18.0.0'} peerDependencies: - '@storybook/test': 8.6.12 + '@storybook/test': 8.6.14 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.6.12 + storybook: ^8.6.14 typescript: '>= 4.2.x' peerDependenciesMeta: '@storybook/test': @@ -3089,13 +3082,13 @@ packages: typescript: optional: true - '@storybook/test@8.6.12': - resolution: {integrity: sha512-0BK1Eg+VD0lNMB1BtxqHE3tP9FdkUmohtvWG7cq6lWvMrbCmAmh3VWai3RMCCDOukPFpjabOr8BBRLVvhNpv2w==} + '@storybook/test@8.6.14': + resolution: {integrity: sha512-GkPNBbbZmz+XRdrhMtkxPotCLOQ1BaGNp/gFZYdGDk2KmUWBKmvc5JxxOhtoXM2703IzNFlQHSSNnhrDZYuLlw==} peerDependencies: - storybook: ^8.6.12 + storybook: ^8.6.14 - '@storybook/theming@8.6.12': - resolution: {integrity: sha512-6VjZg8HJ2Op7+KV7ihJpYrDnFtd9D1jrQnUS8LckcpuBXrIEbaut5+34ObY8ssQnSqkk2GwIZBBBQYQBCVvkOw==} + '@storybook/theming@8.6.14': + resolution: {integrity: sha512-r4y+LsiB37V5hzpQo+BM10PaCsp7YlZ0YcZzQP1OCkPlYXmUAFy2VvDKaFRpD8IeNPKug2u4iFm/laDEbs03dg==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 @@ -3145,65 +3138,65 @@ packages: zod: optional: true - '@tailwindcss/node@4.1.6': - resolution: {integrity: sha512-ed6zQbgmKsjsVvodAS1q1Ld2BolEuxJOSyyNc+vhkjdmfNUDCmQnlXBfQkHrlzNmslxHsQU/bFmzcEbv4xXsLg==} + '@tailwindcss/node@4.1.7': + resolution: {integrity: sha512-9rsOpdY9idRI2NH6CL4wORFY0+Q6fnx9XP9Ju+iq/0wJwGD5IByIgFmwVbyy4ymuyprj8Qh4ErxMKTUL4uNh3g==} - '@tailwindcss/oxide-android-arm64@4.1.6': - resolution: {integrity: sha512-VHwwPiwXtdIvOvqT/0/FLH/pizTVu78FOnI9jQo64kSAikFSZT7K4pjyzoDpSMaveJTGyAKvDjuhxJxKfmvjiQ==} + '@tailwindcss/oxide-android-arm64@4.1.7': + resolution: {integrity: sha512-IWA410JZ8fF7kACus6BrUwY2Z1t1hm0+ZWNEzykKmMNM09wQooOcN/VXr0p/WJdtHZ90PvJf2AIBS/Ceqx1emg==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.1.6': - resolution: {integrity: sha512-weINOCcqv1HVBIGptNrk7c6lWgSFFiQMcCpKM4tnVi5x8OY2v1FrV76jwLukfT6pL1hyajc06tyVmZFYXoxvhQ==} + '@tailwindcss/oxide-darwin-arm64@4.1.7': + resolution: {integrity: sha512-81jUw9To7fimGGkuJ2W5h3/oGonTOZKZ8C2ghm/TTxbwvfSiFSDPd6/A/KE2N7Jp4mv3Ps9OFqg2fEKgZFfsvg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.1.6': - resolution: {integrity: sha512-3FzekhHG0ww1zQjQ1lPoq0wPrAIVXAbUkWdWM8u5BnYFZgb9ja5ejBqyTgjpo5mfy0hFOoMnMuVDI+7CXhXZaQ==} + '@tailwindcss/oxide-darwin-x64@4.1.7': + resolution: {integrity: sha512-q77rWjEyGHV4PdDBtrzO0tgBBPlQWKY7wZK0cUok/HaGgbNKecegNxCGikuPJn5wFAlIywC3v+WMBt0PEBtwGw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.1.6': - resolution: {integrity: sha512-4m5F5lpkBZhVQJq53oe5XgJ+aFYWdrgkMwViHjRsES3KEu2m1udR21B1I77RUqie0ZYNscFzY1v9aDssMBZ/1w==} + '@tailwindcss/oxide-freebsd-x64@4.1.7': + resolution: {integrity: sha512-RfmdbbK6G6ptgF4qqbzoxmH+PKfP4KSVs7SRlTwcbRgBwezJkAO3Qta/7gDy10Q2DcUVkKxFLXUQO6J3CRvBGw==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.6': - resolution: {integrity: sha512-qU0rHnA9P/ZoaDKouU1oGPxPWzDKtIfX7eOGi5jOWJKdxieUJdVV+CxWZOpDWlYTd4N3sFQvcnVLJWJ1cLP5TA==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.7': + resolution: {integrity: sha512-OZqsGvpwOa13lVd1z6JVwQXadEobmesxQ4AxhrwRiPuE04quvZHWn/LnihMg7/XkN+dTioXp/VMu/p6A5eZP3g==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.1.6': - resolution: {integrity: sha512-jXy3TSTrbfgyd3UxPQeXC3wm8DAgmigzar99Km9Sf6L2OFfn/k+u3VqmpgHQw5QNfCpPe43em6Q7V76Wx7ogIQ==} + '@tailwindcss/oxide-linux-arm64-gnu@4.1.7': + resolution: {integrity: sha512-voMvBTnJSfKecJxGkoeAyW/2XRToLZ227LxswLAwKY7YslG/Xkw9/tJNH+3IVh5bdYzYE7DfiaPbRkSHFxY1xA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.1.6': - resolution: {integrity: sha512-8kjivE5xW0qAQ9HX9reVFmZj3t+VmljDLVRJpVBEoTR+3bKMnvC7iLcoSGNIUJGOZy1mLVq7x/gerVg0T+IsYw==} + '@tailwindcss/oxide-linux-arm64-musl@4.1.7': + resolution: {integrity: sha512-PjGuNNmJeKHnP58M7XyjJyla8LPo+RmwHQpBI+W/OxqrwojyuCQ+GUtygu7jUqTEexejZHr/z3nBc/gTiXBj4A==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.1.6': - resolution: {integrity: sha512-A4spQhwnWVpjWDLXnOW9PSinO2PTKJQNRmL/aIl2U/O+RARls8doDfs6R41+DAXK0ccacvRyDpR46aVQJJCoCg==} + '@tailwindcss/oxide-linux-x64-gnu@4.1.7': + resolution: {integrity: sha512-HMs+Va+ZR3gC3mLZE00gXxtBo3JoSQxtu9lobbZd+DmfkIxR54NO7Z+UQNPsa0P/ITn1TevtFxXTpsRU7qEvWg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.1.6': - resolution: {integrity: sha512-YRee+6ZqdzgiQAHVSLfl3RYmqeeaWVCk796MhXhLQu2kJu2COHBkqlqsqKYx3p8Hmk5pGCQd2jTAoMWWFeyG2A==} + '@tailwindcss/oxide-linux-x64-musl@4.1.7': + resolution: {integrity: sha512-MHZ6jyNlutdHH8rd+YTdr3QbXrHXqwIhHw9e7yXEBcQdluGwhpQY2Eku8UZK6ReLaWtQ4gijIv5QoM5eE+qlsA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-wasm32-wasi@4.1.6': - resolution: {integrity: sha512-qAp4ooTYrBQ5pk5jgg54/U1rCJ/9FLYOkkQ/nTE+bVMseMfB6O7J8zb19YTpWuu4UdfRf5zzOrNKfl6T64MNrQ==} + '@tailwindcss/oxide-wasm32-wasi@4.1.7': + resolution: {integrity: sha512-ANaSKt74ZRzE2TvJmUcbFQ8zS201cIPxUDm5qez5rLEwWkie2SkGtA4P+GPTj+u8N6JbPrC8MtY8RmJA35Oo+A==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -3214,24 +3207,24 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.1.6': - resolution: {integrity: sha512-nqpDWk0Xr8ELO/nfRUDjk1pc9wDJ3ObeDdNMHLaymc4PJBWj11gdPCWZFKSK2AVKjJQC7J2EfmSmf47GN7OuLg==} + '@tailwindcss/oxide-win32-arm64-msvc@4.1.7': + resolution: {integrity: sha512-HUiSiXQ9gLJBAPCMVRk2RT1ZrBjto7WvqsPBwUrNK2BcdSxMnk19h4pjZjI7zgPhDxlAbJSumTC4ljeA9y0tEw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.1.6': - resolution: {integrity: sha512-5k9xF33xkfKpo9wCvYcegQ21VwIBU1/qEbYlVukfEIyQbEA47uK8AAwS7NVjNE3vHzcmxMYwd0l6L4pPjjm1rQ==} + '@tailwindcss/oxide-win32-x64-msvc@4.1.7': + resolution: {integrity: sha512-rYHGmvoHiLJ8hWucSfSOEmdCBIGZIq7SpkPRSqLsH2Ab2YUNgKeAPT1Fi2cx3+hnYOrAb0jp9cRyode3bBW4mQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.1.6': - resolution: {integrity: sha512-0bpEBQiGx+227fW4G0fLQ8vuvyy5rsB1YIYNapTq3aRsJ9taF3f5cCaovDjN5pUGKKzcpMrZst/mhNaKAPOHOA==} + '@tailwindcss/oxide@4.1.7': + resolution: {integrity: sha512-5SF95Ctm9DFiUyjUPnDGkoKItPX/k+xifcQhcqX5RA85m50jw1pT/KzjdvlqxRja45Y52nR4MR9fD1JYd7f8NQ==} engines: {node: '>= 10'} - '@tailwindcss/postcss@4.1.6': - resolution: {integrity: sha512-ELq+gDMBuRXPJlpE3PEen+1MhnHAQQrh2zF0dI1NXOlEWfr2qWf2CQdr5jl9yANv8RErQaQ2l6nIFO9OSCVq/g==} + '@tailwindcss/postcss@4.1.7': + resolution: {integrity: sha512-88g3qmNZn7jDgrrcp3ZXEQfp9CVox7xjP1HN2TFKI03CltPVd/c61ydn5qJJL8FYunn0OqBaW5HNUga0kmPVvw==} '@tanstack/query-core@5.76.0': resolution: {integrity: sha512-FN375hb8ctzfNAlex5gHI6+WDXTNpe0nbxp/d2YJtnP+IBM6OUm7zcaoCW6T63BawGOYZBbKC0iPvr41TteNVg==} @@ -3377,8 +3370,8 @@ packages: '@types/node-fetch@2.6.12': resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} - '@types/node@22.15.18': - resolution: {integrity: sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==} + '@types/node@22.15.19': + resolution: {integrity: sha512-3vMNr4TzNQyjHcRZadojpRaD9Ofr6LsonZAoQ+HMUa/9ORTPoxVIw0e0mpqWpdjj8xybyCM+oKOUH2vwFu/oEw==} '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -3673,10 +3666,6 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} - accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} - engines: {node: '>= 0.6'} - acorn-globals@7.0.1: resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} @@ -3939,10 +3928,6 @@ packages: bn.js@5.2.1: resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} - body-parser@2.2.0: - resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} - engines: {node: '>=18'} - boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -4014,10 +3999,6 @@ packages: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -4184,28 +4165,12 @@ packages: constants-browserify@1.0.0: resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} - content-disposition@1.0.0: - resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} - engines: {node: '>= 0.6'} - - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie-signature@1.2.2: - resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} - engines: {node: '>=6.6.0'} - - cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} - engines: {node: '>= 0.6'} - cookie@1.0.2: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} @@ -4231,10 +4196,6 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} - cosmiconfig@7.1.0: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} @@ -4402,10 +4363,6 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -4588,9 +4545,6 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - ejs@3.1.10: resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} engines: {node: '>=0.10.0'} @@ -4629,10 +4583,6 @@ packages: resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} engines: {node: '>= 4'} - encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} - endent@2.1.0: resolution: {integrity: sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w==} @@ -4715,9 +4665,6 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} @@ -4838,8 +4785,8 @@ packages: resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.26.0: - resolution: {integrity: sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==} + eslint@9.27.0: + resolution: {integrity: sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -4883,10 +4830,6 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -4895,14 +4838,6 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - eventsource-parser@3.0.1: - resolution: {integrity: sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==} - engines: {node: '>=18.0.0'} - - eventsource@3.0.6: - resolution: {integrity: sha512-l19WpE2m9hSuyP06+FbuUUf1G+R0SFLrtQfbRb9PRr+oimOfxQhgGCbVaXg5IvZyyTThJsxh6L/srkMiCeBPDA==} - engines: {node: '>=18.0.0'} - evp_bytestokey@1.0.3: resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} @@ -4918,16 +4853,6 @@ packages: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - express-rate-limit@7.5.0: - resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} - engines: {node: '>= 16'} - peerDependencies: - express: ^4.11 || 5 || ^5.0.0-beta.1 - - express@5.1.0: - resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} - engines: {node: '>= 18'} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -4996,10 +4921,6 @@ packages: resolution: {integrity: sha512-lO3ttPjHZRfjMcxWKb1j1eDhTFsu4meeR3lnMcnBFhk6RuLhvEiuALu2TlfL310ph4lCYYwgF/ElIjdP739tdg==} engines: {node: '>=8'} - finalhandler@2.1.0: - resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} - engines: {node: '>= 0.8'} - find-cache-dir@3.3.2: resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} engines: {node: '>=8'} @@ -5066,14 +4987,6 @@ packages: forwarded-parse@2.1.2: resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} - forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - - fresh@2.0.0: - resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} - engines: {node: '>= 0.8'} - fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -5263,10 +5176,6 @@ packages: htmlparser2@6.1.0: resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} - http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} - http-proxy-agent@5.0.0: resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} engines: {node: '>= 6'} @@ -5348,10 +5257,6 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - is-arguments@1.2.0: resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} engines: {node: '>= 0.4'} @@ -5453,9 +5358,6 @@ packages: is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - is-promise@4.0.0: - resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} - is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} @@ -5828,68 +5730,68 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - lightningcss-darwin-arm64@1.29.2: - resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==} + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] - lightningcss-darwin-x64@1.29.2: - resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==} + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] - lightningcss-freebsd-x64@1.29.2: - resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==} + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] - lightningcss-linux-arm-gnueabihf@1.29.2: - resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==} + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] - lightningcss-linux-arm64-gnu@1.29.2: - resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==} + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - lightningcss-linux-arm64-musl@1.29.2: - resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==} + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - lightningcss-linux-x64-gnu@1.29.2: - resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==} + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - lightningcss-linux-x64-musl@1.29.2: - resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==} + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - lightningcss-win32-arm64-msvc@1.29.2: - resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==} + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] - lightningcss-win32-x64-msvc@1.29.2: - resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==} + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] - lightningcss@1.29.2: - resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==} + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} engines: {node: '>= 12.0.0'} lines-and-columns@1.2.4: @@ -5950,8 +5852,8 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lucide-react@0.510.0: - resolution: {integrity: sha512-p8SQRAMVh7NhsAIETokSqDrc5CHnDLbV29mMnzaXx+Vc/hnqQzwI2r0FMWCcoTXnbw2KEjy48xwpGdEL+ck06Q==} + lucide-react@0.511.0: + resolution: {integrity: sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -5997,10 +5899,6 @@ packages: mdn-data@2.0.14: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} - media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} - engines: {node: '>= 0.8'} - memfs@3.5.3: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} @@ -6011,10 +5909,6 @@ packages: memoizerific@1.11.3: resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==} - merge-descriptors@2.0.0: - resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} - engines: {node: '>=18'} - merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -6034,18 +5928,10 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mime-types@3.0.1: - resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} - engines: {node: '>= 0.6'} - mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -6115,10 +6001,6 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - negotiator@1.0.0: - resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} - engines: {node: '>= 0.6'} - neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -6236,10 +6118,6 @@ packages: obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} - on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -6314,10 +6192,6 @@ packages: parse5@7.2.1: resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} - parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - pascal-case@3.1.2: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} @@ -6347,10 +6221,6 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-to-regexp@8.2.0: - resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} - engines: {node: '>=16'} - path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -6397,10 +6267,6 @@ packages: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} - pkce-challenge@5.0.0: - resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} - engines: {node: '>=16.20.0'} - pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} @@ -6512,8 +6378,8 @@ packages: resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==} engines: {node: '>=12'} - posthog-js@1.242.1: - resolution: {integrity: sha512-j2mzw0eukyuw/Qm3tNZ6pfaXmc7eglWj6ftmvR1Lz9GtMr85ndGNXJvIGO+6PBrQW2o0D1G0k/KV93ehta0hFA==} + posthog-js@1.242.3: + resolution: {integrity: sha512-T6fR5Y5y2nW/Q2pv+5WwTBUkocvBkCO87aZRGjjbiaD5wCeW5ofgk/EAkqUrKlasLXNYPo6WTPixp3dHCIJa2Q==} peerDependencies: '@rrweb/types': 2.0.0-alpha.17 rrweb-snapshot: 2.0.0-alpha.17 @@ -6633,10 +6499,6 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -6693,10 +6555,6 @@ packages: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} - raw-body@3.0.0: - resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} - engines: {node: '>= 0.8'} - react-base16-styling@0.9.1: resolution: {integrity: sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==} @@ -6725,8 +6583,8 @@ packages: peerDependencies: react: '>=16.13.1' - react-hook-form@7.56.3: - resolution: {integrity: sha512-IK18V6GVbab4TAo1/cz3kqajxbDPGofdF0w7VHdCo0Nt8PrPlOZcuuDq9YYIV1BtjcX78x0XsldbQRQnQXWXmw==} + react-hook-form@7.56.4: + resolution: {integrity: sha512-Rob7Ftz2vyZ/ZGsQZPaRdIefkgOSrQSPXfqBdvOPwJfoGnjwRJUs7EM7Kc1mcoDv3NOtqBzPGbcMB8CGn9CKgw==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 @@ -6975,10 +6833,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - router@2.2.0: - resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} - engines: {node: '>= 18'} - rtl-css-js@1.16.1: resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} @@ -7060,17 +6914,9 @@ packages: engines: {node: '>=10'} hasBin: true - send@1.2.0: - resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} - engines: {node: '>= 18'} - serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - serve-static@2.2.0: - resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} - engines: {node: '>= 18'} - server-only@0.0.1: resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} @@ -7093,9 +6939,6 @@ packages: setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - sha.js@2.4.11: resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} hasBin: true @@ -7215,15 +7058,11 @@ packages: resolution: {integrity: sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==} engines: {node: '>=6'} - statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} - storybook@8.6.12: - resolution: {integrity: sha512-Z/nWYEHBTLK1ZBtAWdhxC0l5zf7ioJ7G4+zYqtTdYeb67gTnxNj80gehf8o8QY9L2zA2+eyMRGLC2V5fI7Z3Tw==} + storybook@8.6.14: + resolution: {integrity: sha512-sVKbCj/OTx67jhmauhxc2dcr1P+yOgz/x3h0krwjyMgdc5Oubvxyg4NYDZmzAw+ym36g/lzH8N0Ccp4dwtdfxw==} hasBin: true peerDependencies: prettier: ^2 || ^3 @@ -7379,8 +7218,8 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders' - tailwindcss@4.1.6: - resolution: {integrity: sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg==} + tailwindcss@4.1.7: + resolution: {integrity: sha512-kr1o/ErIdNhTz8uzAYL7TpaUuzKIE6QPQ4qmSdxnoX/lo+5wmUHQA6h3L5yIqEImSRnAAURDirLu/BgiXGPAhg==} tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} @@ -7447,10 +7286,6 @@ packages: toggle-selection@1.0.6: resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - tough-cookie@4.1.4: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} @@ -7484,8 +7319,8 @@ packages: ts-easing@0.2.0: resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==} - ts-jest@29.3.3: - resolution: {integrity: sha512-y6jLm19SL4GroiBmHwFK4dSHUfDNmOrJbRfp6QmDIlI9p5tT5Q8ItccB4pTIslCIqOZuQnBwpTR0bQ5eUMYwkw==} + ts-jest@29.3.4: + resolution: {integrity: sha512-Iqbrm8IXOmV+ggWHOTEbjwyCf2xZlUMv5npExksXohL+tk8va4Fjhb+X2+Rt9NBmgO7bJ8WpnMLOwih/DnMlFA==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -7577,18 +7412,10 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} - type-fest@4.40.0: - resolution: {integrity: sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==} - engines: {node: '>=16'} - type-fest@4.41.0: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} - type-is@2.0.1: - resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} - engines: {node: '>= 0.6'} - typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -7648,10 +7475,6 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - unplugin@1.0.1: resolution: {integrity: sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA==} @@ -7747,10 +7570,6 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - vaul@1.1.2: resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} peerDependencies: @@ -7930,11 +7749,6 @@ packages: resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} engines: {node: '>=12.20'} - zod-to-json-schema@3.24.5: - resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} - peerDependencies: - zod: ^3.24.1 - zod-validation-error@3.4.0: resolution: {integrity: sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==} engines: {node: '>=18.0.0'} @@ -8762,23 +8576,23 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@chromatic-com/storybook@3.2.6(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@chromatic-com/storybook@3.2.6(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: chromatic: 11.27.0 filesize: 10.1.6 jsonfile: 6.1.0 react-confetti: 6.4.0(react@19.1.0) - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) strip-ansi: 7.1.0 transitivePeerDependencies: - '@chromatic-com/cypress' - '@chromatic-com/playwright' - react - '@clerk/backend@1.32.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@clerk/backend@1.32.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@clerk/shared': 3.9.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@clerk/types': 4.58.1 + '@clerk/shared': 3.9.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@clerk/types': 4.59.0 cookie: 1.0.2 snakecase-keys: 8.0.1 tslib: 2.8.1 @@ -8786,20 +8600,20 @@ snapshots: - react - react-dom - '@clerk/clerk-react@5.31.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@clerk/clerk-react@5.31.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@clerk/shared': 3.9.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@clerk/types': 4.58.1 + '@clerk/shared': 3.9.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@clerk/types': 4.59.0 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) tslib: 2.8.1 - '@clerk/nextjs@6.19.3(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@clerk/nextjs@6.19.4(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@clerk/backend': 1.32.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@clerk/clerk-react': 5.31.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@clerk/shared': 3.9.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@clerk/types': 4.58.1 + '@clerk/backend': 1.32.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@clerk/clerk-react': 5.31.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@clerk/shared': 3.9.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@clerk/types': 4.59.0 next: 15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) @@ -8808,9 +8622,9 @@ snapshots: transitivePeerDependencies: - svix - '@clerk/shared@3.9.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@clerk/shared@3.9.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@clerk/types': 4.58.1 + '@clerk/types': 4.59.0 dequal: 2.0.3 glob-to-regexp: 0.4.1 js-cookie: 3.0.5 @@ -8820,12 +8634,12 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - '@clerk/themes@2.2.44': + '@clerk/themes@2.2.45': dependencies: - '@clerk/types': 4.58.1 + '@clerk/types': 4.59.0 tslib: 2.8.1 - '@clerk/types@4.58.1': + '@clerk/types@4.59.0': dependencies: csstype: 3.1.3 @@ -8866,11 +8680,6 @@ snapshots: tslib: 2.8.1 optional: true - '@emnapi/runtime@1.3.1': - dependencies: - tslib: 2.8.1 - optional: true - '@emnapi/runtime@1.4.0': dependencies: tslib: 2.8.1 @@ -9096,14 +8905,14 @@ snapshots: '@esbuild/win32-x64@0.25.2': optional: true - '@eslint-community/eslint-utils@4.4.1(eslint@9.26.0(jiti@2.4.2))': + '@eslint-community/eslint-utils@4.4.1(eslint@9.27.0(jiti@2.4.2))': dependencies: - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.7.0(eslint@9.26.0(jiti@2.4.2))': + '@eslint-community/eslint-utils@4.7.0(eslint@9.27.0(jiti@2.4.2))': dependencies: - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -9118,7 +8927,7 @@ snapshots: '@eslint/config-helpers@0.2.1': {} - '@eslint/core@0.13.0': + '@eslint/core@0.14.0': dependencies: '@types/json-schema': 7.0.15 @@ -9136,13 +8945,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.26.0': {} + '@eslint/js@9.27.0': {} '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.2.8': + '@eslint/plugin-kit@0.3.1': dependencies: - '@eslint/core': 0.13.0 + '@eslint/core': 0.14.0 levn: 0.4.1 '@fastify/otel@https://codeload.github.com/getsentry/fastify-otel/tar.gz/ae3088d65e286bdc94ac5d722573537d6a6671bb(@opentelemetry/api@1.9.0)': @@ -9180,10 +8989,10 @@ snapshots: '@floating-ui/utils@0.2.9': {} - '@hookform/resolvers@5.0.1(react-hook-form@7.56.3(react@19.1.0))': + '@hookform/resolvers@5.0.1(react-hook-form@7.56.4(react@19.1.0))': dependencies: '@standard-schema/utils': 0.3.0 - react-hook-form: 7.56.3(react@19.1.0) + react-hook-form: 7.56.4(react@19.1.0) '@humanfs/core@0.19.1': {} @@ -9331,7 +9140,7 @@ snapshots: '@img/sharp-wasm32@0.33.5': dependencies: - '@emnapi/runtime': 1.3.1 + '@emnapi/runtime': 1.4.0 optional: true '@img/sharp-wasm32@0.34.1': @@ -9368,27 +9177,27 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.18 + '@types/node': 22.15.19 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3))': + '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.18 + '@types/node': 22.15.19 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) + jest-config: 29.7.0(@types/node@22.15.19)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -9413,7 +9222,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.18 + '@types/node': 22.15.19 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -9431,7 +9240,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.15.18 + '@types/node': 22.15.19 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -9453,7 +9262,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 22.15.18 + '@types/node': 22.15.19 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -9523,7 +9332,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.15.18 + '@types/node': 22.15.19 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -9573,7 +9382,7 @@ snapshots: react-number-format: 5.4.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-remove-scroll: 2.6.3(@types/react@19.1.4)(react@19.1.0) react-textarea-autosize: 8.5.9(@types/react@19.1.4)(react@19.1.0) - type-fest: 4.40.0 + type-fest: 4.41.0 transitivePeerDependencies: - '@types/react' @@ -9587,21 +9396,6 @@ snapshots: '@types/react': 19.1.4 react: 19.1.0 - '@modelcontextprotocol/sdk@1.11.0': - dependencies: - content-type: 1.0.5 - cors: 2.8.5 - cross-spawn: 7.0.6 - eventsource: 3.0.6 - express: 5.1.0 - express-rate-limit: 7.5.0(express@5.1.0) - pkce-challenge: 5.0.0 - raw-body: 3.0.0 - zod: 3.24.4 - zod-to-json-schema: 3.24.5(zod@3.24.4) - transitivePeerDependencies: - - supports-color - '@napi-rs/wasm-runtime@0.2.9': dependencies: '@emnapi/core': 1.4.3 @@ -9751,7 +9545,7 @@ snapshots: '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 forwarded-parse: 2.1.2 - semver: 7.7.1 + semver: 7.7.2 transitivePeerDependencies: - supports-color @@ -9876,7 +9670,7 @@ snapshots: '@types/shimmer': 1.2.0 import-in-the-middle: 1.13.2 require-in-the-middle: 7.5.1 - semver: 7.7.1 + semver: 7.7.2 shimmer: 1.2.1 transitivePeerDependencies: - supports-color @@ -10721,100 +10515,100 @@ snapshots: '@standard-schema/utils@0.3.0': {} - '@storybook/addon-actions@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/addon-actions@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: '@storybook/global': 5.0.0 '@types/uuid': 9.0.8 dequal: 2.0.3 polished: 4.3.1 - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) uuid: 9.0.1 - '@storybook/addon-backgrounds@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/addon-backgrounds@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: '@storybook/global': 5.0.0 memoizerific: 1.11.3 - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) ts-dedent: 2.2.0 - '@storybook/addon-controls@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/addon-controls@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: '@storybook/global': 5.0.0 dequal: 2.0.3 - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) ts-dedent: 2.2.0 - '@storybook/addon-docs@8.6.12(@types/react@19.1.4)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/addon-docs@8.6.14(@types/react@19.1.4)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: '@mdx-js/react': 3.1.0(@types/react@19.1.4)(react@19.1.0) - '@storybook/blocks': 8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - '@storybook/csf-plugin': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - '@storybook/react-dom-shim': 8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/blocks': 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/csf-plugin': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/react-dom-shim': 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-essentials@8.6.12(@types/react@19.1.4)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': - dependencies: - '@storybook/addon-actions': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - '@storybook/addon-backgrounds': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - '@storybook/addon-controls': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - '@storybook/addon-docs': 8.6.12(@types/react@19.1.4)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - '@storybook/addon-highlight': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - '@storybook/addon-measure': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - '@storybook/addon-outline': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - '@storybook/addon-toolbars': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - '@storybook/addon-viewport': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + '@storybook/addon-essentials@8.6.14(@types/react@19.1.4)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': + dependencies: + '@storybook/addon-actions': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/addon-backgrounds': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/addon-controls': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/addon-docs': 8.6.14(@types/react@19.1.4)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/addon-highlight': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/addon-measure': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/addon-outline': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/addon-toolbars': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/addon-viewport': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-highlight@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/addon-highlight@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) - '@storybook/addon-measure@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/addon-measure@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) tiny-invariant: 1.3.3 - '@storybook/addon-onboarding@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/addon-onboarding@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) - '@storybook/addon-outline@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/addon-outline@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) ts-dedent: 2.2.0 - '@storybook/addon-toolbars@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/addon-toolbars@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) - '@storybook/addon-viewport@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/addon-viewport@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: memoizerific: 1.11.3 - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) - '@storybook/blocks@8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/blocks@8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: '@storybook/icons': 1.4.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) ts-dedent: 2.2.0 optionalDependencies: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - '@storybook/builder-webpack5@8.6.12(esbuild@0.25.2)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3)': + '@storybook/builder-webpack5@8.6.14(esbuild@0.25.2)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3)': dependencies: - '@storybook/core-webpack': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/core-webpack': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) '@types/semver': 7.7.0 browser-assert: 1.2.1 case-sensitive-paths-webpack-plugin: 2.4.0 @@ -10827,8 +10621,8 @@ snapshots: magic-string: 0.30.17 path-browserify: 1.0.1 process: 0.11.10 - semver: 7.7.1 - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + semver: 7.7.2 + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) style-loader: 3.3.4(webpack@5.98.0(esbuild@0.25.2)) terser-webpack-plugin: 5.3.11(esbuild@0.25.2)(webpack@5.98.0(esbuild@0.25.2)) ts-dedent: 2.2.0 @@ -10848,18 +10642,18 @@ snapshots: - uglify-js - webpack-cli - '@storybook/components@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/components@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) - '@storybook/core-webpack@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/core-webpack@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) ts-dedent: 2.2.0 - '@storybook/core@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/core@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: - '@storybook/theming': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/theming': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) better-opn: 3.0.2 browser-assert: 1.2.1 esbuild: 0.25.2 @@ -10867,7 +10661,7 @@ snapshots: jsdoc-type-pratt-parser: 4.1.0 process: 0.11.10 recast: 0.23.11 - semver: 7.7.1 + semver: 7.7.2 util: 0.12.5 ws: 8.18.0(bufferutil@4.0.9) optionalDependencies: @@ -10878,9 +10672,9 @@ snapshots: - supports-color - utf-8-validate - '@storybook/csf-plugin@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/csf-plugin@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) unplugin: 1.16.1 '@storybook/csf@0.1.13': @@ -10894,17 +10688,17 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - '@storybook/instrumenter@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/instrumenter@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: '@storybook/global': 5.0.0 '@vitest/utils': 2.1.9 - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) - '@storybook/manager-api@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/manager-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) - '@storybook/nextjs@8.6.12(babel-plugin-macros@3.1.0)(esbuild@0.25.2)(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(type-fest@4.41.0)(typescript@5.8.3)(webpack-hot-middleware@2.26.1)(webpack@5.98.0(esbuild@0.25.2))': + '@storybook/nextjs@8.6.14(babel-plugin-macros@3.1.0)(esbuild@0.25.2)(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))(type-fest@4.41.0)(typescript@5.8.3)(webpack-hot-middleware@2.26.1)(webpack@5.98.0(esbuild@0.25.2))': dependencies: '@babel/core': 7.26.9 '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.9) @@ -10920,10 +10714,10 @@ snapshots: '@babel/preset-typescript': 7.27.0(@babel/core@7.26.9) '@babel/runtime': 7.26.10 '@pmmmwh/react-refresh-webpack-plugin': 0.5.16(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.98.0(esbuild@0.25.2)) - '@storybook/builder-webpack5': 8.6.12(esbuild@0.25.2)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3) - '@storybook/preset-react-webpack': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)))(esbuild@0.25.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3) - '@storybook/react': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3) - '@storybook/test': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/builder-webpack5': 8.6.14(esbuild@0.25.2)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3) + '@storybook/preset-react-webpack': 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)))(esbuild@0.25.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3) + '@storybook/react': 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3) + '@storybook/test': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) '@types/semver': 7.7.0 babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.2)) css-loader: 6.11.0(webpack@5.98.0(esbuild@0.25.2)) @@ -10940,8 +10734,8 @@ snapshots: react-refresh: 0.14.2 resolve-url-loader: 5.0.0 sass-loader: 14.2.1(webpack@5.98.0(esbuild@0.25.2)) - semver: 7.7.1 - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + semver: 7.7.2 + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) style-loader: 3.3.4(webpack@5.98.0(esbuild@0.25.2)) styled-jsx: 5.1.6(@babel/core@7.26.9)(babel-plugin-macros@3.1.0)(react@19.1.0) ts-dedent: 2.2.0 @@ -10969,10 +10763,10 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/preset-react-webpack@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)))(esbuild@0.25.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3)': + '@storybook/preset-react-webpack@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)))(esbuild@0.25.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3)': dependencies: - '@storybook/core-webpack': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - '@storybook/react': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3) + '@storybook/core-webpack': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/react': 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3) '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.2)) '@types/semver': 7.7.0 find-up: 5.0.0 @@ -10981,8 +10775,8 @@ snapshots: react-docgen: 7.1.1 react-dom: 19.1.0(react@19.1.0) resolve: 1.22.10 - semver: 7.7.1 - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + semver: 7.7.2 + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) tsconfig-paths: 4.2.0 webpack: 5.98.0(esbuild@0.25.2) optionalDependencies: @@ -10995,9 +10789,9 @@ snapshots: - uglify-js - webpack-cli - '@storybook/preview-api@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/preview-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.2))': dependencies: @@ -11013,41 +10807,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@storybook/react-dom-shim@8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/react-dom-shim@8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) - '@storybook/react@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3)': + '@storybook/react@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))(typescript@5.8.3)': dependencies: - '@storybook/components': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/components': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) '@storybook/global': 5.0.0 - '@storybook/manager-api': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - '@storybook/preview-api': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - '@storybook/react-dom-shim': 8.6.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) - '@storybook/theming': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/manager-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/preview-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/react-dom-shim': 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/theming': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) optionalDependencies: - '@storybook/test': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/test': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) typescript: 5.8.3 - '@storybook/test@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: '@storybook/global': 5.0.0 - '@storybook/instrumenter': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/instrumenter': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) '@testing-library/dom': 10.4.0 '@testing-library/jest-dom': 6.5.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) '@vitest/expect': 2.0.5 '@vitest/spy': 2.0.5 - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) - '@storybook/theming@8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3))': + '@storybook/theming@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3))': dependencies: - storybook: 8.6.12(bufferutil@4.0.9)(prettier@3.5.3) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3) '@stripe/react-stripe-js@3.7.0(@stripe/stripe-js@7.3.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: @@ -11080,77 +10874,77 @@ snapshots: transitivePeerDependencies: - arktype - '@tailwindcss/node@4.1.6': + '@tailwindcss/node@4.1.7': dependencies: '@ampproject/remapping': 2.3.0 enhanced-resolve: 5.18.1 jiti: 2.4.2 - lightningcss: 1.29.2 + lightningcss: 1.30.1 magic-string: 0.30.17 source-map-js: 1.2.1 - tailwindcss: 4.1.6 + tailwindcss: 4.1.7 - '@tailwindcss/oxide-android-arm64@4.1.6': + '@tailwindcss/oxide-android-arm64@4.1.7': optional: true - '@tailwindcss/oxide-darwin-arm64@4.1.6': + '@tailwindcss/oxide-darwin-arm64@4.1.7': optional: true - '@tailwindcss/oxide-darwin-x64@4.1.6': + '@tailwindcss/oxide-darwin-x64@4.1.7': optional: true - '@tailwindcss/oxide-freebsd-x64@4.1.6': + '@tailwindcss/oxide-freebsd-x64@4.1.7': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.6': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.7': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.1.6': + '@tailwindcss/oxide-linux-arm64-gnu@4.1.7': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.1.6': + '@tailwindcss/oxide-linux-arm64-musl@4.1.7': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.1.6': + '@tailwindcss/oxide-linux-x64-gnu@4.1.7': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.1.6': + '@tailwindcss/oxide-linux-x64-musl@4.1.7': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.1.6': + '@tailwindcss/oxide-wasm32-wasi@4.1.7': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.1.6': + '@tailwindcss/oxide-win32-arm64-msvc@4.1.7': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.1.6': + '@tailwindcss/oxide-win32-x64-msvc@4.1.7': optional: true - '@tailwindcss/oxide@4.1.6': + '@tailwindcss/oxide@4.1.7': dependencies: detect-libc: 2.0.4 tar: 7.4.3 optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.1.6 - '@tailwindcss/oxide-darwin-arm64': 4.1.6 - '@tailwindcss/oxide-darwin-x64': 4.1.6 - '@tailwindcss/oxide-freebsd-x64': 4.1.6 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.6 - '@tailwindcss/oxide-linux-arm64-gnu': 4.1.6 - '@tailwindcss/oxide-linux-arm64-musl': 4.1.6 - '@tailwindcss/oxide-linux-x64-gnu': 4.1.6 - '@tailwindcss/oxide-linux-x64-musl': 4.1.6 - '@tailwindcss/oxide-wasm32-wasi': 4.1.6 - '@tailwindcss/oxide-win32-arm64-msvc': 4.1.6 - '@tailwindcss/oxide-win32-x64-msvc': 4.1.6 - - '@tailwindcss/postcss@4.1.6': + '@tailwindcss/oxide-android-arm64': 4.1.7 + '@tailwindcss/oxide-darwin-arm64': 4.1.7 + '@tailwindcss/oxide-darwin-x64': 4.1.7 + '@tailwindcss/oxide-freebsd-x64': 4.1.7 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.7 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.7 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.7 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.7 + '@tailwindcss/oxide-linux-x64-musl': 4.1.7 + '@tailwindcss/oxide-wasm32-wasi': 4.1.7 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.7 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.7 + + '@tailwindcss/postcss@4.1.7': dependencies: '@alloc/quick-lru': 5.2.0 - '@tailwindcss/node': 4.1.6 - '@tailwindcss/oxide': 4.1.6 + '@tailwindcss/node': 4.1.7 + '@tailwindcss/oxide': 4.1.7 postcss: 8.5.3 - tailwindcss: 4.1.6 + tailwindcss: 4.1.7 '@tanstack/query-core@5.76.0': {} @@ -11254,7 +11048,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 22.15.18 + '@types/node': 22.15.19 '@types/doctrine@0.0.9': {} @@ -11272,7 +11066,7 @@ snapshots: '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 22.15.18 + '@types/node': 22.15.19 '@types/html-minifier-terser@6.1.0': {} @@ -11295,7 +11089,7 @@ snapshots: '@types/jsdom@20.0.1': dependencies: - '@types/node': 22.15.18 + '@types/node': 22.15.19 '@types/tough-cookie': 4.0.5 parse5: 7.2.1 @@ -11309,14 +11103,14 @@ snapshots: '@types/mysql@2.15.26': dependencies: - '@types/node': 22.15.18 + '@types/node': 22.15.19 '@types/node-fetch@2.6.12': dependencies: - '@types/node': 22.15.18 + '@types/node': 22.15.19 form-data: 4.0.2 - '@types/node@22.15.18': + '@types/node@22.15.19': dependencies: undici-types: 6.21.0 @@ -11328,13 +11122,13 @@ snapshots: '@types/pg@8.11.6': dependencies: - '@types/node': 22.15.18 + '@types/node': 22.15.19 pg-protocol: 1.7.1 pg-types: 4.0.2 '@types/pg@8.6.1': dependencies: - '@types/node': 22.15.18 + '@types/node': 22.15.19 pg-protocol: 1.7.1 pg-types: 2.2.0 @@ -11360,7 +11154,7 @@ snapshots: '@types/tedious@4.0.14': dependencies: - '@types/node': 22.15.18 + '@types/node': 22.15.19 '@types/tough-cookie@4.0.5': {} @@ -11372,15 +11166,15 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/scope-manager': 8.32.0 - '@typescript-eslint/type-utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/type-utils': 8.32.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.32.0 - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -11389,15 +11183,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/scope-manager': 8.32.1 - '@typescript-eslint/type-utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/type-utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.32.1 - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) graphemer: 1.4.0 ignore: 7.0.4 natural-compare: 1.4.0 @@ -11406,26 +11200,26 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/parser@8.32.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@typescript-eslint/scope-manager': 8.32.0 '@typescript-eslint/types': 8.32.0 '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.32.0 debug: 4.4.0 - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@typescript-eslint/scope-manager': 8.32.1 '@typescript-eslint/types': 8.32.1 '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.32.1 debug: 4.4.0 - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -11445,23 +11239,23 @@ snapshots: '@typescript-eslint/types': 8.32.1 '@typescript-eslint/visitor-keys': 8.32.1 - '@typescript-eslint/type-utils@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.32.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) debug: 4.4.0 - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) - '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) debug: 4.4.0 - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: @@ -11515,35 +11309,35 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.29.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/utils@8.29.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.26.0(jiti@2.4.2)) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.27.0(jiti@2.4.2)) '@typescript-eslint/scope-manager': 8.29.0 '@typescript-eslint/types': 8.29.0 '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.3) - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/utils@8.32.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@2.4.2)) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.27.0(jiti@2.4.2)) '@typescript-eslint/scope-manager': 8.32.0 '@typescript-eslint/types': 8.32.0 '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.8.3) - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/utils@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@2.4.2)) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.27.0(jiti@2.4.2)) '@typescript-eslint/scope-manager': 8.32.1 '@typescript-eslint/types': 8.32.1 '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3) - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -11704,11 +11498,6 @@ snapshots: dependencies: event-target-shim: 5.0.1 - accepts@2.0.0: - dependencies: - mime-types: 3.0.1 - negotiator: 1.0.0 - acorn-globals@7.0.1: dependencies: acorn: 8.14.0 @@ -12030,20 +11819,6 @@ snapshots: bn.js@5.2.1: {} - body-parser@2.2.0: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 4.4.0 - http-errors: 2.0.0 - iconv-lite: 0.6.3 - on-finished: 2.4.1 - qs: 6.14.0 - raw-body: 3.0.0 - type-is: 2.0.1 - transitivePeerDependencies: - - supports-color - boolbase@1.0.0: {} brace-expansion@1.1.11: @@ -12142,8 +11917,6 @@ snapshots: dependencies: streamsearch: 1.1.0 - bytes@3.1.2: {} - call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -12295,20 +12068,10 @@ snapshots: constants-browserify@1.0.0: {} - content-disposition@1.0.0: - dependencies: - safe-buffer: 5.2.1 - - content-type@1.0.5: {} - convert-source-map@1.9.0: {} convert-source-map@2.0.0: {} - cookie-signature@1.2.2: {} - - cookie@0.7.2: {} - cookie@1.0.2: {} cookies-next@5.1.0(next@15.3.2(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0): @@ -12331,11 +12094,6 @@ snapshots: core-util-is@1.0.3: {} - cors@2.8.5: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - cosmiconfig@7.1.0: dependencies: '@types/parse-json': 4.0.2 @@ -12375,13 +12133,13 @@ snapshots: safe-buffer: 5.2.1 sha.js: 2.4.11 - create-jest@29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)): + create-jest@29.7.0(@types/node@22.15.19)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) + jest-config: 29.7.0(@types/node@22.15.19)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -12430,7 +12188,7 @@ snapshots: postcss-modules-scope: 3.2.1(postcss@8.5.3) postcss-modules-values: 4.0.0(postcss@8.5.3) postcss-value-parser: 4.2.0 - semver: 7.7.1 + semver: 7.7.2 optionalDependencies: webpack: 5.98.0(esbuild@0.25.2) @@ -12527,8 +12285,6 @@ snapshots: delayed-stream@1.0.0: {} - depd@2.0.0: {} - dequal@2.0.3: {} des.js@1.1.0: @@ -12536,7 +12292,8 @@ snapshots: inherits: 2.0.4 minimalistic-assert: 1.0.1 - detect-libc@2.0.3: {} + detect-libc@2.0.3: + optional: true detect-libc@2.0.4: {} @@ -12631,8 +12388,6 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - ee-first@1.1.1: {} - ejs@3.1.10: dependencies: jake: 10.9.2 @@ -12669,8 +12424,6 @@ snapshots: emojis-list@3.0.0: {} - encodeurl@2.0.0: {} - endent@2.1.0: dependencies: dedent: 0.7.0 @@ -12861,8 +12614,6 @@ snapshots: escalade@3.2.0: {} - escape-html@1.0.3: {} - escape-string-regexp@2.0.0: {} escape-string-regexp@4.0.0: {} @@ -12875,19 +12626,19 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-next@15.3.2(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3): + eslint-config-next@15.3.2(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3): dependencies: '@next/eslint-plugin-next': 15.3.2 '@rushstack/eslint-patch': 1.10.5 - '@typescript-eslint/eslint-plugin': 8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - eslint: 9.26.0(jiti@2.4.2) + '@typescript-eslint/eslint-plugin': 8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + eslint: 9.27.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.26.0(jiti@2.4.2)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.26.0(jiti@2.4.2)) - eslint-plugin-react: 7.37.4(eslint@9.26.0(jiti@2.4.2)) - eslint-plugin-react-hooks: 5.1.0(eslint@9.26.0(jiti@2.4.2)) + eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-react: 7.37.4(eslint@9.27.0(jiti@2.4.2)) + eslint-plugin-react-hooks: 5.1.0(eslint@9.27.0(jiti@2.4.2)) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -12895,9 +12646,9 @@ snapshots: - eslint-plugin-import-x - supports-color - eslint-config-prettier@10.1.5(eslint@9.26.0(jiti@2.4.2)): + eslint-config-prettier@10.1.5(eslint@9.27.0(jiti@2.4.2)): dependencies: - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) eslint-import-resolver-node@0.3.9: dependencies: @@ -12907,37 +12658,37 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.26.0(jiti@2.4.2)): + eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.27.0(jiti@2.4.2)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.0 enhanced-resolve: 5.18.1 - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) fast-glob: 3.3.3 get-tsconfig: 4.10.0 is-bun-module: 1.3.0 is-glob: 4.0.3 stable-hash: 0.0.4 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.26.0(jiti@2.4.2)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.27.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - eslint: 9.26.0(jiti@2.4.2) + '@typescript-eslint/parser': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + eslint: 9.27.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-drizzle@0.2.3(eslint@9.26.0(jiti@2.4.2)): + eslint-plugin-drizzle@0.2.3(eslint@9.27.0(jiti@2.4.2)): dependencies: - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -12946,9 +12697,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.26.0(jiti@2.4.2)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.27.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -12960,13 +12711,13 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.26.0(jiti@2.4.2)): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.27.0(jiti@2.4.2)): dependencies: aria-query: 5.3.2 array-includes: 3.1.8 @@ -12976,7 +12727,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -12985,11 +12736,11 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-react-hooks@5.1.0(eslint@9.26.0(jiti@2.4.2)): + eslint-plugin-react-hooks@5.1.0(eslint@9.27.0(jiti@2.4.2)): dependencies: - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) - eslint-plugin-react@7.37.4(eslint@9.26.0(jiti@2.4.2)): + eslint-plugin-react@7.37.4(eslint@9.27.0(jiti@2.4.2)): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 @@ -12997,7 +12748,7 @@ snapshots: array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.2.1 - eslint: 9.26.0(jiti@2.4.2) + eslint: 9.27.0(jiti@2.4.2) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -13011,11 +12762,11 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-storybook@0.12.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3): + eslint-plugin-storybook@0.12.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3): dependencies: '@storybook/csf': 0.1.13 - '@typescript-eslint/utils': 8.29.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - eslint: 9.26.0(jiti@2.4.2) + '@typescript-eslint/utils': 8.29.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + eslint: 9.27.0(jiti@2.4.2) ts-dedent: 2.2.0 transitivePeerDependencies: - supports-color @@ -13035,20 +12786,19 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint@9.26.0(jiti@2.4.2): + eslint@9.27.0(jiti@2.4.2): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.26.0(jiti@2.4.2)) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.27.0(jiti@2.4.2)) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.20.0 '@eslint/config-helpers': 0.2.1 - '@eslint/core': 0.13.0 + '@eslint/core': 0.14.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.26.0 - '@eslint/plugin-kit': 0.2.8 + '@eslint/js': 9.27.0 + '@eslint/plugin-kit': 0.3.1 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.2 - '@modelcontextprotocol/sdk': 1.11.0 '@types/estree': 1.0.6 '@types/json-schema': 7.0.15 ajv: 6.12.6 @@ -13073,7 +12823,6 @@ snapshots: minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.4 - zod: 3.24.4 optionalDependencies: jiti: 2.4.2 transitivePeerDependencies: @@ -13107,18 +12856,10 @@ snapshots: esutils@2.0.3: {} - etag@1.8.1: {} - event-target-shim@5.0.1: {} events@3.3.0: {} - eventsource-parser@3.0.1: {} - - eventsource@3.0.6: - dependencies: - eventsource-parser: 3.0.1 - evp_bytestokey@1.0.3: dependencies: md5.js: 1.3.5 @@ -13146,42 +12887,6 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 - express-rate-limit@7.5.0(express@5.1.0): - dependencies: - express: 5.1.0 - - express@5.1.0: - dependencies: - accepts: 2.0.0 - body-parser: 2.2.0 - content-disposition: 1.0.0 - content-type: 1.0.5 - cookie: 0.7.2 - cookie-signature: 1.2.2 - debug: 4.4.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 2.1.0 - fresh: 2.0.0 - http-errors: 2.0.0 - merge-descriptors: 2.0.0 - mime-types: 3.0.1 - on-finished: 2.4.1 - once: 1.4.0 - parseurl: 1.3.3 - proxy-addr: 2.0.7 - qs: 6.14.0 - range-parser: 1.2.1 - router: 2.2.0 - send: 1.2.0 - serve-static: 2.2.0 - statuses: 2.0.1 - type-is: 2.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - fast-deep-equal@3.1.3: {} fast-glob@3.3.1: @@ -13246,17 +12951,6 @@ snapshots: filter-obj@2.0.2: {} - finalhandler@2.1.0: - dependencies: - debug: 4.4.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.1 - transitivePeerDependencies: - - supports-color - find-cache-dir@3.3.2: dependencies: commondir: 1.0.1 @@ -13316,7 +13010,7 @@ snapshots: minimatch: 3.1.2 node-abort-controller: 3.1.1 schema-utils: 3.3.0 - semver: 7.7.1 + semver: 7.7.2 tapable: 2.2.1 typescript: 5.8.3 webpack: 5.98.0(esbuild@0.25.2) @@ -13334,10 +13028,6 @@ snapshots: forwarded-parse@2.1.2: {} - forwarded@0.2.0: {} - - fresh@2.0.0: {} - fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 @@ -13540,14 +13230,6 @@ snapshots: domutils: 2.8.0 entities: 2.2.0 - http-errors@2.0.0: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 - http-proxy-agent@5.0.0: dependencies: '@tootallnate/once': 2.0.0 @@ -13627,8 +13309,6 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 - ipaddr.js@1.9.1: {} - is-arguments@1.2.0: dependencies: call-bound: 1.0.3 @@ -13727,8 +13407,6 @@ snapshots: is-potential-custom-element-name@1.0.1: {} - is-promise@4.0.0: {} - is-reference@1.2.1: dependencies: '@types/estree': 1.0.6 @@ -13805,7 +13483,7 @@ snapshots: '@babel/parser': 7.27.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.7.1 + semver: 7.7.2 transitivePeerDependencies: - supports-color @@ -13858,7 +13536,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.18 + '@types/node': 22.15.19 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.3(babel-plugin-macros@3.1.0) @@ -13878,16 +13556,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)): + jest-cli@29.7.0(@types/node@22.15.19)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) + create-jest: 29.7.0(@types/node@22.15.19)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) + jest-config: 29.7.0(@types/node@22.15.19)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -13897,7 +13575,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)): + jest-config@29.7.0(@types/node@22.15.19)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3)): dependencies: '@babel/core': 7.26.9 '@jest/test-sequencer': 29.7.0 @@ -13922,8 +13600,8 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 22.15.18 - ts-node: 10.9.2(@types/node@22.15.18)(typescript@5.8.3) + '@types/node': 22.15.19 + ts-node: 10.9.2(@types/node@22.15.19)(typescript@5.8.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -13953,7 +13631,7 @@ snapshots: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 22.15.18 + '@types/node': 22.15.19 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3(bufferutil@4.0.9) @@ -13967,7 +13645,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.18 + '@types/node': 22.15.19 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -13977,7 +13655,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 22.15.18 + '@types/node': 22.15.19 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -14016,7 +13694,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.18 + '@types/node': 22.15.19 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -14051,7 +13729,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.18 + '@types/node': 22.15.19 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -14079,7 +13757,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.18 + '@types/node': 22.15.19 chalk: 4.1.2 cjs-module-lexer: 1.4.3 collect-v8-coverage: 1.0.2 @@ -14118,14 +13796,14 @@ snapshots: jest-util: 29.7.0 natural-compare: 1.4.0 pretty-format: 29.7.0 - semver: 7.7.1 + semver: 7.7.2 transitivePeerDependencies: - supports-color jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.18 + '@types/node': 22.15.19 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -14144,7 +13822,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.18 + '@types/node': 22.15.19 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -14153,23 +13831,23 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 22.15.18 + '@types/node': 22.15.19 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 22.15.18 + '@types/node': 22.15.19 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)): + jest@29.7.0(@types/node@22.15.19)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) + jest-cli: 29.7.0(@types/node@22.15.19)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -14180,13 +13858,13 @@ snapshots: jiti@2.4.2: {} - jotai-devtools@0.12.0(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(redux@5.0.1)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)): + jotai-devtools@0.12.0(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(redux@5.0.1)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)): dependencies: '@mantine/code-highlight': 7.17.7(@mantine/core@7.17.7(@mantine/hooks@7.17.7(react@19.1.0))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@7.17.7(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@mantine/core': 7.17.7(@mantine/hooks@7.17.7(react@19.1.0))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@mantine/hooks': 7.17.7(react@19.1.0) '@redux-devtools/extension': 3.3.0(redux@5.0.1) - '@storybook/test': 8.6.12(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/test': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) clsx: 2.1.1 javascript-stringify: 2.1.0 jotai: 2.12.4(@types/react@19.1.4)(react@19.1.0) @@ -14301,10 +13979,10 @@ snapshots: kleur@3.0.3: {} - knip@5.56.0(@types/node@22.15.18)(typescript@5.8.3): + knip@5.56.0(@types/node@22.15.19)(typescript@5.8.3): dependencies: '@nodelib/fs.walk': 1.2.8 - '@types/node': 22.15.18 + '@types/node': 22.15.19 fast-glob: 3.3.3 formatly: 0.2.3 jiti: 2.4.2 @@ -14332,50 +14010,50 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - lightningcss-darwin-arm64@1.29.2: + lightningcss-darwin-arm64@1.30.1: optional: true - lightningcss-darwin-x64@1.29.2: + lightningcss-darwin-x64@1.30.1: optional: true - lightningcss-freebsd-x64@1.29.2: + lightningcss-freebsd-x64@1.30.1: optional: true - lightningcss-linux-arm-gnueabihf@1.29.2: + lightningcss-linux-arm-gnueabihf@1.30.1: optional: true - lightningcss-linux-arm64-gnu@1.29.2: + lightningcss-linux-arm64-gnu@1.30.1: optional: true - lightningcss-linux-arm64-musl@1.29.2: + lightningcss-linux-arm64-musl@1.30.1: optional: true - lightningcss-linux-x64-gnu@1.29.2: + lightningcss-linux-x64-gnu@1.30.1: optional: true - lightningcss-linux-x64-musl@1.29.2: + lightningcss-linux-x64-musl@1.30.1: optional: true - lightningcss-win32-arm64-msvc@1.29.2: + lightningcss-win32-arm64-msvc@1.30.1: optional: true - lightningcss-win32-x64-msvc@1.29.2: + lightningcss-win32-x64-msvc@1.30.1: optional: true - lightningcss@1.29.2: + lightningcss@1.30.1: dependencies: - detect-libc: 2.0.3 + detect-libc: 2.0.4 optionalDependencies: - lightningcss-darwin-arm64: 1.29.2 - lightningcss-darwin-x64: 1.29.2 - lightningcss-freebsd-x64: 1.29.2 - lightningcss-linux-arm-gnueabihf: 1.29.2 - lightningcss-linux-arm64-gnu: 1.29.2 - lightningcss-linux-arm64-musl: 1.29.2 - lightningcss-linux-x64-gnu: 1.29.2 - lightningcss-linux-x64-musl: 1.29.2 - lightningcss-win32-arm64-msvc: 1.29.2 - lightningcss-win32-x64-msvc: 1.29.2 + lightningcss-darwin-arm64: 1.30.1 + lightningcss-darwin-x64: 1.30.1 + lightningcss-freebsd-x64: 1.30.1 + lightningcss-linux-arm-gnueabihf: 1.30.1 + lightningcss-linux-arm64-gnu: 1.30.1 + lightningcss-linux-arm64-musl: 1.30.1 + lightningcss-linux-x64-gnu: 1.30.1 + lightningcss-linux-x64-musl: 1.30.1 + lightningcss-win32-arm64-msvc: 1.30.1 + lightningcss-win32-x64-msvc: 1.30.1 lines-and-columns@1.2.4: {} @@ -14427,7 +14105,7 @@ snapshots: dependencies: yallist: 3.1.1 - lucide-react@0.510.0(react@19.1.0): + lucide-react@0.511.0(react@19.1.0): dependencies: react: 19.1.0 @@ -14447,7 +14125,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.1 + semver: 7.7.2 make-error@1.3.6: {} @@ -14469,8 +14147,6 @@ snapshots: mdn-data@2.0.14: {} - media-typer@1.1.0: {} - memfs@3.5.3: dependencies: fs-monkey: 1.0.6 @@ -14481,8 +14157,6 @@ snapshots: dependencies: map-or-similar: 1.5.0 - merge-descriptors@2.0.0: {} - merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -14499,16 +14173,10 @@ snapshots: mime-db@1.52.0: {} - mime-db@1.54.0: {} - mime-types@2.1.35: dependencies: mime-db: 1.52.0 - mime-types@3.0.1: - dependencies: - mime-db: 1.54.0 - mimic-fn@2.1.0: {} min-indent@1.0.1: {} @@ -14566,8 +14234,6 @@ snapshots: natural-compare@1.4.0: {} - negotiator@1.0.0: {} - neo-async@2.6.2: {} next-themes@0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): @@ -14709,10 +14375,6 @@ snapshots: obuf@1.1.2: {} - on-finished@2.4.1: - dependencies: - ee-first: 1.1.1 - once@1.4.0: dependencies: wrappy: 1.0.2 @@ -14817,8 +14479,6 @@ snapshots: dependencies: entities: 4.5.0 - parseurl@1.3.3: {} - pascal-case@3.1.2: dependencies: no-case: 3.0.4 @@ -14841,8 +14501,6 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 - path-to-regexp@8.2.0: {} - path-type@4.0.0: {} pathval@2.0.0: {} @@ -14887,8 +14545,6 @@ snapshots: pirates@4.0.7: {} - pkce-challenge@5.0.0: {} - pkg-dir@4.2.0: dependencies: find-up: 4.1.0 @@ -14914,7 +14570,7 @@ snapshots: cosmiconfig: 9.0.0(typescript@5.8.3) jiti: 1.21.7 postcss: 8.5.3 - semver: 7.7.1 + semver: 7.7.2 optionalDependencies: webpack: 5.98.0(esbuild@0.25.2) transitivePeerDependencies: @@ -14984,7 +14640,7 @@ snapshots: postgres@3.4.5: {} - posthog-js@1.242.1: + posthog-js@1.242.3: dependencies: core-js: 3.41.0 fflate: 0.4.8 @@ -15048,11 +14704,6 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 - proxy-addr@2.0.7: - dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - proxy-from-env@1.1.0: {} psl@1.15.0: @@ -15116,13 +14767,6 @@ snapshots: range-parser@1.2.1: {} - raw-body@3.0.0: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.6.3 - unpipe: 1.0.0 - react-base16-styling@0.9.1: dependencies: '@babel/runtime': 7.26.10 @@ -15167,7 +14811,7 @@ snapshots: '@babel/runtime': 7.26.10 react: 19.1.0 - react-hook-form@7.56.3(react@19.1.0): + react-hook-form@7.56.4(react@19.1.0): dependencies: react: 19.1.0 @@ -15487,16 +15131,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.35.0 fsevents: 2.3.3 - router@2.2.0: - dependencies: - debug: 4.4.0 - depd: 2.0.0 - is-promise: 4.0.0 - parseurl: 1.3.3 - path-to-regexp: 8.2.0 - transitivePeerDependencies: - - supports-color - rtl-css-js@1.16.1: dependencies: '@babel/runtime': 7.26.10 @@ -15563,35 +15197,10 @@ snapshots: semver@7.7.2: {} - send@1.2.0: - dependencies: - debug: 4.4.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 2.0.0 - http-errors: 2.0.0 - mime-types: 3.0.1 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.1 - transitivePeerDependencies: - - supports-color - serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 - serve-static@2.2.0: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 1.2.0 - transitivePeerDependencies: - - supports-color - server-only@0.0.1: {} set-function-length@1.2.2: @@ -15620,8 +15229,6 @@ snapshots: setimmediate@1.0.5: {} - setprototypeof@1.2.0: {} - sha.js@2.4.11: dependencies: inherits: 2.0.4 @@ -15630,8 +15237,8 @@ snapshots: sharp@0.33.5: dependencies: color: 4.2.3 - detect-libc: 2.0.3 - semver: 7.7.1 + detect-libc: 2.0.4 + semver: 7.7.2 optionalDependencies: '@img/sharp-darwin-arm64': 0.33.5 '@img/sharp-darwin-x64': 0.33.5 @@ -15658,7 +15265,7 @@ snapshots: dependencies: color: 4.2.3 detect-libc: 2.0.3 - semver: 7.7.1 + semver: 7.7.2 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.1 '@img/sharp-darwin-x64': 0.34.1 @@ -15742,7 +15349,7 @@ snapshots: dependencies: map-obj: 4.3.0 snake-case: 3.0.4 - type-fest: 4.40.0 + type-fest: 4.41.0 source-map-js@1.2.1: {} @@ -15793,13 +15400,11 @@ snapshots: dependencies: type-fest: 0.7.1 - statuses@2.0.1: {} - std-env@3.9.0: {} - storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3): + storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3): dependencies: - '@storybook/core': 8.6.12(bufferutil@4.0.9)(prettier@3.5.3)(storybook@8.6.12(bufferutil@4.0.9)(prettier@3.5.3)) + '@storybook/core': 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)) optionalDependencies: prettier: 3.5.3 transitivePeerDependencies: @@ -15916,11 +15521,11 @@ snapshots: strip-json-comments@5.0.1: {} - stripe@18.1.0(@types/node@22.15.18): + stripe@18.1.0(@types/node@22.15.19): dependencies: qs: 6.14.0 optionalDependencies: - '@types/node': 22.15.18 + '@types/node': 22.15.19 style-loader@3.3.4(webpack@5.98.0(esbuild@0.25.2)): dependencies: @@ -15960,11 +15565,11 @@ snapshots: tailwind-merge@3.3.0: {} - tailwindcss-animate@1.0.7(tailwindcss@4.1.6): + tailwindcss-animate@1.0.7(tailwindcss@4.1.7): dependencies: - tailwindcss: 4.1.6 + tailwindcss: 4.1.7 - tailwindcss@4.1.6: {} + tailwindcss@4.1.7: {} tapable@2.2.1: {} @@ -16023,8 +15628,6 @@ snapshots: toggle-selection@1.0.6: {} - toidentifier@1.0.1: {} - tough-cookie@4.1.4: dependencies: psl: 1.15.0 @@ -16052,12 +15655,12 @@ snapshots: ts-easing@0.2.0: {} - ts-jest@29.3.3(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.9))(esbuild@0.25.2)(jest@29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)))(typescript@5.8.3): + ts-jest@29.3.4(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.9))(esbuild@0.25.2)(jest@29.7.0(@types/node@22.15.19)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3)))(typescript@5.8.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.15.18)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)) + jest: 29.7.0(@types/node@22.15.19)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -16073,14 +15676,14 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.26.9) esbuild: 0.25.2 - ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3): + ts-node@10.9.2(@types/node@22.15.19)(typescript@5.8.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.15.18 + '@types/node': 22.15.19 acorn: 8.14.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -16137,16 +15740,8 @@ snapshots: type-fest@2.19.0: {} - type-fest@4.40.0: {} - type-fest@4.41.0: {} - type-is@2.0.1: - dependencies: - content-type: 1.0.5 - media-typer: 1.1.0 - mime-types: 3.0.1 - typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.3 @@ -16180,12 +15775,12 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3): + typescript-eslint@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/parser': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/utils': 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - eslint: 9.26.0(jiti@2.4.2) + '@typescript-eslint/eslint-plugin': 8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) + eslint: 9.27.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -16216,8 +15811,6 @@ snapshots: universalify@2.0.1: {} - unpipe@1.0.0: {} - unplugin@1.0.1: dependencies: acorn: 8.14.0 @@ -16310,8 +15903,6 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 - vary@1.1.2: {} - vaul@1.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@radix-ui/react-dialog': 1.1.13(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -16512,10 +16103,6 @@ snapshots: yocto-queue@1.2.1: {} - zod-to-json-schema@3.24.5(zod@3.24.4): - dependencies: - zod: 3.24.4 - zod-validation-error@3.4.0(zod@3.24.4): dependencies: zod: 3.24.4 diff --git a/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts b/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts index 57d193b..34e66f2 100644 --- a/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts +++ b/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts @@ -2,7 +2,6 @@ import { auth } from '@clerk/nextjs/server'; import { eq } from 'drizzle-orm'; -import { revalidatePath } from 'next/cache'; import { notifyOrderUpdated } from '~/app/api/realtime/notifications'; import { type LocationId } from '~/domain/locations'; import { AppError } from '~/lib/error-utils.server'; @@ -67,6 +66,6 @@ export async function markOrderItemAsDeliveredAction(locationId: LocationId, ord })), }); - revalidatePath(`/u/${locationId}/live`); //TODO + // revalidatePath(`/u/${locationId}/live`); //TODO return order; } diff --git a/src/app/u/[locationId]/live/_components/OrderCard.tsx b/src/app/u/[locationId]/live/_components/OrderCard.tsx index e52e3f2..fb73da0 100644 --- a/src/app/u/[locationId]/live/_components/OrderCard.tsx +++ b/src/app/u/[locationId]/live/_components/OrderCard.tsx @@ -48,7 +48,9 @@ export function OrderCard({ className="flex items-center justify-between border-b pb-2" >
-

{menuItemsMap.get(item.menuItem.id)?.name ?? 'Unknown Item'}

+

+ [{item.orderItem.id}]{menuItemsMap.get(item.menuItem.id)?.name ?? 'Unknown Item'} +

${menuItemsMap.get(item.menuItem.id)?.price ?? 'Unknown Item'}

diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts index 11febfe..5475b96 100644 --- a/src/server/db/schema.ts +++ b/src/server/db/schema.ts @@ -165,7 +165,6 @@ export const orderItems = createTable('order_item', { updatedAt: timestamp('updated_at', { withTimezone: true }).$onUpdate(() => new Date()), }); -// optional: add relations for `.with` export const ordersRelations = relations(orders, ({ many }) => ({ orderItems: many(orderItems), })); diff --git a/src/server/queries/orders.ts b/src/server/queries/orders.ts index 38be5e8..af5466a 100644 --- a/src/server/queries/orders.ts +++ b/src/server/queries/orders.ts @@ -96,19 +96,24 @@ export async function getOpenOrdersByLocation(locationId: LocationId): Promise

({ - menuItem: { - id: orderItem.menuItemId, - // You may need to fetch menuItem name and price here if not included in orderItem - name: '', // TODO Placeholder, replace with actual value if available - price: '0', // TODO Placeholder, replace with actual value if available - }, - orderItem: { - id: orderItem.id, - isDelivered: orderItem.isDelivered, - isPaid: orderItem.isPaid, - }, - })), + items: order.orderItems + .map((orderItem) => ({ + menuItem: { + id: orderItem.menuItemId, + // You may need to fetch menuItem name and price here if not included in orderItem + name: '', // TODO Placeholder, replace with actual value if available + price: '0', // TODO Placeholder, replace with actual value if available + }, + orderItem: { + id: orderItem.id, + isDelivered: orderItem.isDelivered, + isPaid: orderItem.isPaid, + createdAt: orderItem.createdAt, + }, + })) + .sort((a, b) => { + return a.orderItem.id - b.orderItem.id; + }), })); return ordersWithItems; } From 180cf78f77024b1906a1cc4bdd6bc1240d0077bf Mon Sep 17 00:00:00 2001 From: dopoto Date: Mon, 19 May 2025 11:11:03 +0300 Subject: [PATCH 30/47] works --- .../markOrderItemAsDeliveredAction.ts | 11 +-- src/server/queries/locations.ts | 14 +++- src/server/queries/orders.ts | 76 +++++++++++-------- 3 files changed, 58 insertions(+), 43 deletions(-) diff --git a/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts b/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts index 34e66f2..a5aa95f 100644 --- a/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts +++ b/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts @@ -1,7 +1,7 @@ 'use server'; -import { auth } from '@clerk/nextjs/server'; import { eq } from 'drizzle-orm'; +import { revalidateTag } from 'next/cache'; import { notifyOrderUpdated } from '~/app/api/realtime/notifications'; import { type LocationId } from '~/domain/locations'; import { AppError } from '~/lib/error-utils.server'; @@ -15,11 +15,6 @@ import { getOrderById } from '~/server/queries/orders'; // }; export async function markOrderItemAsDeliveredAction(locationId: LocationId, orderItemId: number) { - const { userId } = await auth(); //TODO needed? - if (!userId) { - throw new AppError({ internalMessage: 'Unauthorized' }); - } - // Verify that the location belongs to the current user's organization await getLocationForCurrentUserOrThrow(locationId); @@ -49,7 +44,6 @@ export async function markOrderItemAsDeliveredAction(locationId: LocationId, ord if (!order) { throw new AppError({ internalMessage: 'Order not found after update' }); } - await notifyOrderUpdated(locationId, { ...order, items: order.items.map((item) => ({ @@ -66,6 +60,7 @@ export async function markOrderItemAsDeliveredAction(locationId: LocationId, ord })), }); - // revalidatePath(`/u/${locationId}/live`); //TODO + // Revalidate data without causing a page refresh + revalidateTag(`location-${locationId}-orders`); return order; } diff --git a/src/server/queries/locations.ts b/src/server/queries/locations.ts index 9f9e653..b6b7a7c 100644 --- a/src/server/queries/locations.ts +++ b/src/server/queries/locations.ts @@ -63,15 +63,23 @@ export async function getLocationForCurrentUserOrThrow(locationId: string | numb } const validLocationId = locationIdValidationResult.data; + // Get the auth data outside the cache function const { userId, sessionClaims } = await auth(); + if (!userId) { - throw new AppError({ internalMessage: 'Unauthorized' }); + throw new AppError({ internalMessage: 'Unauthorized - no user ID provided' }); + } + + const clerkOrgId = sessionClaims?.org_id; + + if (!clerkOrgId) { + throw new AppError({ internalMessage: 'Unauthorized - no organization ID provided' }); } - const validClerkOrgId = getValidClerkOrgIdOrThrow(sessionClaims?.org_id); + const validClerkOrgId = getValidClerkOrgIdOrThrow(clerkOrgId); if (!validClerkOrgId) { throw new AppError({ - internalMessage: `No valid clerk org id found in session claims - ${JSON.stringify(sessionClaims)}.`, + internalMessage: `Invalid organization ID: ${clerkOrgId}`, }); } diff --git a/src/server/queries/orders.ts b/src/server/queries/orders.ts index af5466a..9daf55a 100644 --- a/src/server/queries/orders.ts +++ b/src/server/queries/orders.ts @@ -1,4 +1,5 @@ import { sql } from 'drizzle-orm'; +import { unstable_cache } from 'next/cache'; import { z } from 'zod'; import type { LocationId } from '~/domain/locations'; import { PublicOrderItem } from '~/domain/order-items'; @@ -82,41 +83,52 @@ export async function createOrder(data: z.infer): Promis }); } -export async function getOpenOrdersByLocation(locationId: LocationId): Promise { +export const getOpenOrdersByLocation = async (locationId: LocationId): Promise => { + // Validate location access before caching const validLocation = await getLocationForCurrentUserOrThrow(locationId); - const items = await db.query.orders.findMany({ - where: (orders, { eq }) => eq(orders.locationId, validLocation.id), - with: { - orderItems: true, - }, - }); - const ordersWithItems: PublicOrderWithItems[] = items.map((order) => ({ - id: order.id, - locationId: order.locationId, - createdAt: order.createdAt, - updatedAt: order.updatedAt, - items: order.orderItems - .map((orderItem) => ({ - menuItem: { - id: orderItem.menuItemId, - // You may need to fetch menuItem name and price here if not included in orderItem - name: '', // TODO Placeholder, replace with actual value if available - price: '0', // TODO Placeholder, replace with actual value if available - }, - orderItem: { - id: orderItem.id, - isDelivered: orderItem.isDelivered, - isPaid: orderItem.isPaid, - createdAt: orderItem.createdAt, + return unstable_cache( + async () => { + // Use the locationId directly since we've already validated it + const items = await db.query.orders.findMany({ + where: (orders, { eq }) => eq(orders.locationId, validLocation.id), + with: { + orderItems: true, }, - })) - .sort((a, b) => { - return a.orderItem.id - b.orderItem.id; - }), - })); - return ordersWithItems; -} + }); + + const ordersWithItems: PublicOrderWithItems[] = items.map((order) => ({ + id: order.id, + locationId: order.locationId, + createdAt: order.createdAt, + updatedAt: order.updatedAt, + items: order.orderItems + .map((orderItem) => ({ + menuItem: { + id: orderItem.menuItemId, + name: '', // TODO Placeholder, replace with actual value if available + price: '0', // TODO Placeholder, replace with actual value if available + }, + orderItem: { + id: orderItem.id, + isDelivered: orderItem.isDelivered, + isPaid: orderItem.isPaid, + createdAt: orderItem.createdAt, + }, + })) + .sort((a, b) => { + return a.orderItem.id - b.orderItem.id; + }), + })); + return ordersWithItems; + }, + [`location-${locationId}-orders`], + { + tags: [`location-${locationId}-orders`], + revalidate: 60, // Cache for 60 seconds + }, + )(); +}; export async function getOrderById(locationId: LocationId, orderId: OrderId): Promise { const validLocation = await getLocationForCurrentUserOrThrow(locationId); From 46567d32b157974995b2ff7cb44ed3eb43baa065 Mon Sep 17 00:00:00 2001 From: dopoto Date: Mon, 19 May 2025 11:54:22 +0300 Subject: [PATCH 31/47] wip --- src/app/actions/placeOrderAction.ts | 3 +++ src/app/api/realtime/notifications.ts | 1 + .../live/_actions/markOrderItemAsDeliveredAction.ts | 5 +++-- src/app/u/[locationId]/live/_components/LiveOrders.tsx | 2 ++ src/domain/tags.ts | 5 +++++ src/hooks/use-real-time.ts | 2 ++ src/server/queries/orders.ts | 5 +++-- 7 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 src/domain/tags.ts diff --git a/src/app/actions/placeOrderAction.ts b/src/app/actions/placeOrderAction.ts index 1abdeea..ac242a8 100644 --- a/src/app/actions/placeOrderAction.ts +++ b/src/app/actions/placeOrderAction.ts @@ -38,6 +38,9 @@ export const placeOrderAction = async ( await notifyOrderCreated(parsedForm.data.locationId, orderWithItems); + // TODO causes public page refresh, losing the order + //revalidateTag(TAGS.locationOpenOrders(parsedForm.data.locationId)); + return { status: 'success' as const, fields: { orderWithItems }, diff --git a/src/app/api/realtime/notifications.ts b/src/app/api/realtime/notifications.ts index c6452ab..91097ef 100644 --- a/src/app/api/realtime/notifications.ts +++ b/src/app/api/realtime/notifications.ts @@ -3,6 +3,7 @@ import { type PublicOrderWithItems } from '~/domain/orders'; import { CHANNELS, EVENTS, getPusherServer } from '~/lib/pusher'; export async function notifyOrderCreated(locationId: LocationId, order: PublicOrderWithItems) { + console.log(`DBG-notifyOrderCreated`, JSON.stringify(order)); const pusher = getPusherServer(); // Notify all clients listening to this location about the new order await pusher.trigger(CHANNELS.location(locationId), EVENTS.ORDER_CREATED, order); diff --git a/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts b/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts index a5aa95f..b71711c 100644 --- a/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts +++ b/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts @@ -4,6 +4,7 @@ import { eq } from 'drizzle-orm'; import { revalidateTag } from 'next/cache'; import { notifyOrderUpdated } from '~/app/api/realtime/notifications'; import { type LocationId } from '~/domain/locations'; +import { TAGS } from '~/domain/tags'; import { AppError } from '~/lib/error-utils.server'; import { db } from '~/server/db'; import { orderItems } from '~/server/db/schema'; @@ -60,7 +61,7 @@ export async function markOrderItemAsDeliveredAction(locationId: LocationId, ord })), }); - // Revalidate data without causing a page refresh - revalidateTag(`location-${locationId}-orders`); + revalidateTag(TAGS.locationOpenOrders(locationId)); + return order; } diff --git a/src/app/u/[locationId]/live/_components/LiveOrders.tsx b/src/app/u/[locationId]/live/_components/LiveOrders.tsx index 49d0cef..c9d7bad 100644 --- a/src/app/u/[locationId]/live/_components/LiveOrders.tsx +++ b/src/app/u/[locationId]/live/_components/LiveOrders.tsx @@ -27,6 +27,7 @@ export function LiveOrders({ // Handle new orders locationChannel.bind(EVENTS.ORDER_CREATED, (data: PublicOrderWithItems) => { + console.log(`DBG-EVENTS.ORDER_CREATED`); setOrders((current) => [...current, data]); toast({ title: 'New Order Received', @@ -36,6 +37,7 @@ export function LiveOrders({ // Handle updates to any order locationChannel.bind(EVENTS.ORDER_UPDATED, (data: PublicOrderWithItems) => { + console.log(`DBG-EVENTS.ORDER_UPDATED`); setOrders((current) => current.map((order) => (order.id === data.id ? data : order))); }); diff --git a/src/domain/tags.ts b/src/domain/tags.ts new file mode 100644 index 0000000..273947d --- /dev/null +++ b/src/domain/tags.ts @@ -0,0 +1,5 @@ +import type { LocationId } from '~/domain/locations'; + +export const TAGS = { + locationOpenOrders: (locationId: LocationId) => `location-${locationId}-open-orders`, +}; diff --git a/src/hooks/use-real-time.ts b/src/hooks/use-real-time.ts index 296c8ea..8db50f4 100644 --- a/src/hooks/use-real-time.ts +++ b/src/hooks/use-real-time.ts @@ -26,6 +26,7 @@ export function useRealTimeOrderUpdates(orderId: string | undefined, locationId: // Handle new orders in the location locationChannel.bind(EVENTS.ORDER_CREATED, (data: PublicOrderWithItems) => { + console.log(`DBG-useRealTimeOrderUpdates`, JSON.stringify(data)); toast({ title: 'New Order', description: `Order #${orderId} has been created`, @@ -65,6 +66,7 @@ export function useRealTimeLocationUpdates(locationId: number) { // Handle new orders in the location locationChannel.bind(EVENTS.ORDER_CREATED, (data: PublicOrderWithItems) => { + console.log(`DBG-useRealTimeLocationUpdates`, JSON.stringify(data)); toast({ title: 'New Order', description: `Order #${data.id} has been created`, diff --git a/src/server/queries/orders.ts b/src/server/queries/orders.ts index 9daf55a..73a94b1 100644 --- a/src/server/queries/orders.ts +++ b/src/server/queries/orders.ts @@ -4,6 +4,7 @@ import { z } from 'zod'; import type { LocationId } from '~/domain/locations'; import { PublicOrderItem } from '~/domain/order-items'; import { orderFormSchema, OrderId, PublicOrderWithItems } from '~/domain/orders'; +import { TAGS } from '~/domain/tags'; import { AppError } from '~/lib/error-utils.server'; import { db } from '~/server/db'; import { orderItems, orders } from '~/server/db/schema'; @@ -122,9 +123,9 @@ export const getOpenOrdersByLocation = async (locationId: LocationId): Promise

Date: Mon, 19 May 2025 12:22:59 +0300 Subject: [PATCH 32/47] update order --- src/app/actions/updateOrderAction.ts | 63 +++++++++++++++++ .../_components/PublicFooterPostpaidMode.tsx | 58 ++++++++++++++-- src/server/queries/orders.ts | 67 +++++++++++++++---- 3 files changed, 169 insertions(+), 19 deletions(-) create mode 100644 src/app/actions/updateOrderAction.ts diff --git a/src/app/actions/updateOrderAction.ts b/src/app/actions/updateOrderAction.ts new file mode 100644 index 0000000..3337601 --- /dev/null +++ b/src/app/actions/updateOrderAction.ts @@ -0,0 +1,63 @@ +'use server'; + +import * as Sentry from '@sentry/nextjs'; +import { headers } from 'next/headers'; +import { z } from 'zod'; +import { notifyOrderUpdated } from '~/app/api/realtime/notifications'; +import { menuFormSchema } from '~/domain/menus'; +import { orderFormSchema, type PublicOrderWithItems } from '~/domain/orders'; +import { AppError } from '~/lib/error-utils.server'; +import { type FormState, processFormErrors } from '~/lib/form-state'; +import { updateOrder } from '~/server/queries/orders'; + +export const updateOrderAction = async ( + data: z.infer, +): Promise> => { + 'use server'; + return await Sentry.withServerActionInstrumentation( + 'updateOrderAction', + { + headers: headers(), + recordResponse: true, + }, + async () => { + try { + const parsedForm = orderFormSchema.safeParse(data); + if (!parsedForm.success) { + return processFormErrors(parsedForm.error, data); + } + + const availableQuota = 1; // TODO = await getAvailableFeatureQuota('menus'); + if (availableQuota <= 0) { + return { + status: 'error' as const, + rootError: 'Out of quota for orders.', + }; + } + const orderWithItems = await updateOrder(parsedForm.data); + + await notifyOrderUpdated(parsedForm.data.locationId, orderWithItems); + + // TODO causes public page refresh, losing the order + //revalidateTag(TAGS.locationOpenOrders(parsedForm.data.locationId)); + + return { + status: 'success' as const, + fields: { orderWithItems }, + }; + } catch (error) { + if (error instanceof AppError) { + return { + status: 'error' as const, + rootError: error.publicMessage, + }; + } else { + return { + status: 'error' as const, + rootError: 'An error occurred while processing the order.', + }; + } + } + }, + ); +}; diff --git a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx index 26c9e4a..9548beb 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx @@ -5,6 +5,7 @@ import { ChevronsDownIcon, ChevronsUpIcon } from 'lucide-react'; import Image from 'next/image'; import { useState } from 'react'; import { placeOrderAction } from '~/app/actions/placeOrderAction'; +import { updateOrderAction } from '~/app/actions/updateOrderAction'; import { OrderItemsList } from '~/app/p/[locationSlug]/_components/OrderItemsList'; import { PublicFooterDrawer } from '~/app/p/[locationSlug]/_components/PublicFooterDrawer'; import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; @@ -38,7 +39,7 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati const totalAmount = order.items.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0'), 0); - const processOrder = async (e?: React.MouseEvent) => { + const createOrder = async (e?: React.MouseEvent) => { if (e) { e.stopPropagation(); e.preventDefault(); @@ -78,17 +79,62 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati } }; + const updateOrder = async (e?: React.MouseEvent) => { + if (e) { + e.stopPropagation(); + e.preventDefault(); + } + + try { + setIsLoading(true); + + const res = await updateOrderAction(order); + if (res.status === 'success') { + const orderWithItems = res.fields?.orderWithItems; + toast({ + title: 'Order updated successfully', + description: `Your order number is ${orderWithItems?.id}`, + variant: 'default', + className: getTopPositionedToast(), + }); + setOrder((prevOrder) => { + return { + ...prevOrder, + orderId: orderWithItems?.id ? String(orderWithItems.id) : undefined, //TODO review + items: orderWithItems?.items ?? [], + }; + }); + } else { + toast({ + title: 'Failed to update order', + description: 'Please try again', + variant: 'destructive', + className: getTopPositionedToast(), + }); + } + } catch (error) { + console.error('Failed to update order:', error); + } finally { + setIsLoading(false); + } + }; + const draftItems = order.items.filter((item) => !item.orderItem.id); const inPreparationItems = order.items.filter((item) => item.orderItem.id && item.orderItem.isDelivered === false); const deliveredItems = order.items.filter((item) => item.orderItem.id && item.orderItem.isDelivered === true); const draftItemsSummary = ( - {draftItems.length > 0 && ( - - )} + {draftItems.length > 0 && + (order.orderId ? ( + + ) : ( + + ))} ); const inPreparationItemsSummary = ( diff --git a/src/server/queries/orders.ts b/src/server/queries/orders.ts index 73a94b1..ef126f0 100644 --- a/src/server/queries/orders.ts +++ b/src/server/queries/orders.ts @@ -31,19 +31,6 @@ export async function createOrder(data: z.infer): Promis throw new AppError({ internalMessage: 'Could not insert order' }); } - // if (data.items) { - // for (let i = 0; i < data.items.length; i++) { - // const item = data.items[i]; - // await tx.insert(orderItems).values({ - // orderId: order.id, - // menuItemId: item!.menuItem.id, - // isDelivered: false, - // isPaid: false, - // createdAt: sql`CURRENT_TIMESTAMP`, - // updatedAt: sql`CURRENT_TIMESTAMP`, - // }); - // } - // } const insertedItems: PublicOrderItem[] = []; if (data.items) { for (let i = 0; i < data.items.length; i++) { @@ -84,6 +71,60 @@ export async function createOrder(data: z.infer): Promis }); } +export async function updateOrder(data: z.infer): Promise { + return await db.transaction(async (tx) => { + const itemsToInsert = data.items?.filter((item) => item.orderItem.id === undefined) ?? []; + const itemsAlreadyOrdered = data.items?.filter((item) => item.orderItem.id !== undefined) ?? []; + const insertedItems: PublicOrderItem[] = []; + if (itemsToInsert) { + for (let i = 0; i < itemsToInsert.length; i++) { + const item = itemsToInsert[i]; + const [insertedItem] = await tx + .insert(orderItems) + .values({ + orderId: Number(data.orderId), //TODO review + menuItemId: item!.menuItem.id, + isDelivered: false, + isPaid: false, + createdAt: sql`CURRENT_TIMESTAMP`, + updatedAt: sql`CURRENT_TIMESTAMP`, + }) + .returning(); + if (insertedItem) { + const insertedPublicItem: PublicOrderItem = { + menuItem: { + id: item!.menuItem.id, + name: item!.menuItem.name, + price: item!.menuItem.price, + }, + orderItem: { + id: insertedItem.id, + isDelivered: false, + isPaid: false, + }, + }; + insertedItems.push(insertedPublicItem); + } + } + } + + const [order] = await tx + .select() + .from(orders) + .where(sql`${orders.id} = ${Number(data.orderId)}`); + + if (!order) { + throw new AppError({ internalMessage: 'Order not found after update' }); + } + + const orderWithItems: PublicOrderWithItems = { + ...order, + items: [...itemsAlreadyOrdered, ...insertedItems], + }; + return orderWithItems; + }); +} + export const getOpenOrdersByLocation = async (locationId: LocationId): Promise => { // Validate location access before caching const validLocation = await getLocationForCurrentUserOrThrow(locationId); From dacf10bb95001c5292eeac32d1814bdcddffdf18 Mon Sep 17 00:00:00 2001 From: dopoto Date: Mon, 19 May 2025 13:11:59 +0300 Subject: [PATCH 33/47] refactor --- src/app/actions/createCartPaymentIntent.ts | 2 +- .../_components/JotaiProviderWrapper.tsx | 14 +++++-- .../_components/OrderItemsList.tsx | 8 +++- .../_components/PublicFooterPostpaidMode.tsx | 4 +- .../_components/PublicFooterPrepaidMode.tsx | 38 +++++++++++-------- .../[locationSlug]/_state/menu-items-atom.ts | 5 +++ .../_state/{cart.ts => order-atom.ts} | 0 src/app/p/[locationSlug]/layout.tsx | 6 ++- .../markOrderItemAsDeliveredAction.ts | 6 +-- .../live/_components/OpenOrdersList.tsx | 2 +- .../live/_components/OrderCard.tsx | 6 +-- src/components/public/PublicMenuItem.tsx | 6 +-- src/domain/order-items.ts | 4 +- src/hooks/use-real-time.ts | 2 +- src/server/queries/orders.ts | 29 +++----------- 15 files changed, 70 insertions(+), 62 deletions(-) create mode 100644 src/app/p/[locationSlug]/_state/menu-items-atom.ts rename src/app/p/[locationSlug]/_state/{cart.ts => order-atom.ts} (100%) diff --git a/src/app/actions/createCartPaymentIntent.ts b/src/app/actions/createCartPaymentIntent.ts index 1d2d7c5..edaddb2 100644 --- a/src/app/actions/createCartPaymentIntent.ts +++ b/src/app/actions/createCartPaymentIntent.ts @@ -26,7 +26,7 @@ export async function createCartPaymentIntent( const menuItems = await getMenuItemsByLocation(locationId); const totalAmount = cartItems.reduce((sum, item) => { - const matchedItem = menuItems.find((mi) => mi.id === item.menuItem.id); + const matchedItem = menuItems.find((mi) => mi.id === item.menuItemId); const matchedItemPrice = matchedItem ? matchedItem.price : '0'; //TODO handle? return sum + parseFloat(matchedItemPrice); }, 0); diff --git a/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx b/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx index 6d1f186..4ad6f91 100644 --- a/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx +++ b/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx @@ -3,13 +3,15 @@ import { Provider, useSetAtom } from 'jotai'; import 'jotai-devtools/styles.css'; import { ReactNode, useEffect } from 'react'; -import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; +import { orderAtom } from '~/app/p/[locationSlug]/_state/order-atom'; import { CurrencyId } from '~/domain/currencies'; import { LocationId } from '~/domain/locations'; import type { DevToolsProps } from 'jotai-devtools'; import dynamic from 'next/dynamic'; import type { ComponentType } from 'react'; +import { menuItemsAtom } from '~/app/p/[locationSlug]/_state/menu-items-atom'; +import type { MenuItem } from '~/domain/menu-items'; let DevTools: ComponentType | null = null; @@ -17,13 +19,18 @@ if (process.env.NODE_ENV !== 'production') { DevTools = dynamic(() => import('./JotaiDevTools').then((mod) => ({ default: mod.DevTools })), { ssr: false }); } -function Initializer(props: { locationId: LocationId; currencyId: CurrencyId }) { +function Initializer(props: { locationId: LocationId; currencyId: CurrencyId; menuItems: MenuItem[] }) { const setOrder = useSetAtom(orderAtom); + const setMenuItems = useSetAtom(menuItemsAtom); useEffect(() => { setOrder({ locationId: props.locationId, currencyId: props.currencyId, items: [] }); }, [props.locationId, setOrder]); + useEffect(() => { + setMenuItems(props.menuItems); + }, [props.menuItems, setMenuItems]); + return null; } @@ -31,10 +38,11 @@ export default function JotaiProviderWrapper(props: { children: ReactNode; locationId: LocationId; currencyId: CurrencyId; + menuItems: MenuItem[]; }) { return ( - + {DevTools && } {props.children} diff --git a/src/app/p/[locationSlug]/_components/OrderItemsList.tsx b/src/app/p/[locationSlug]/_components/OrderItemsList.tsx index 1632e62..c9c922b 100644 --- a/src/app/p/[locationSlug]/_components/OrderItemsList.tsx +++ b/src/app/p/[locationSlug]/_components/OrderItemsList.tsx @@ -1,18 +1,22 @@ import { useAtom } from 'jotai'; -import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; +import { menuItemsAtom } from '~/app/p/[locationSlug]/_state/menu-items-atom'; +import { orderAtom } from '~/app/p/[locationSlug]/_state/order-atom'; import { CURRENCIES } from '~/domain/currencies'; import { PublicOrderItem } from '~/domain/order-items'; export function OrderItemsList(props: { items: PublicOrderItem[] }) { const [order] = useAtom(orderAtom); + const [menuItems] = useAtom(menuItemsAtom); const currency = CURRENCIES[order.currencyId]; return ( <> {props.items?.map((item, index) => { + const menuItem = menuItems[item.menuItemId] ?? { name: 'Unknown item', price: 0 }; + const { name, price } = menuItem; return (

- 1 x {item.menuItem.name}, {item.menuItem.price} {currency?.symbol} + 1 x {name}, {price} {currency?.symbol}
); })} diff --git a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx index 9548beb..23bfac3 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx @@ -8,7 +8,7 @@ import { placeOrderAction } from '~/app/actions/placeOrderAction'; import { updateOrderAction } from '~/app/actions/updateOrderAction'; import { OrderItemsList } from '~/app/p/[locationSlug]/_components/OrderItemsList'; import { PublicFooterDrawer } from '~/app/p/[locationSlug]/_components/PublicFooterDrawer'; -import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; +import { orderAtom } from '~/app/p/[locationSlug]/_state/order-atom'; import { Labeled } from '~/components/Labeled'; import { Button } from '~/components/ui/button'; import { DrawerClose } from '~/components/ui/drawer'; @@ -37,7 +37,7 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati // Add real-time updates useRealTimeOrderUpdates(order.orderId, props.locationId); - const totalAmount = order.items.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0'), 0); + //const totalAmount = order.items.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0'), 0); const createOrder = async (e?: React.MouseEvent) => { if (e) { diff --git a/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx index 121aef5..33fcae8 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPrepaidMode.tsx @@ -4,7 +4,8 @@ import { useAtom } from 'jotai'; import { LoaderIcon, ShoppingCart } from 'lucide-react'; import { useState } from 'react'; import { createCartPaymentIntent } from '~/app/actions/createCartPaymentIntent'; -import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; +import { menuItemsAtom } from '~/app/p/[locationSlug]/_state/menu-items-atom'; +import { orderAtom } from '~/app/p/[locationSlug]/_state/order-atom'; import { PaymentButton } from '~/components/public/PaymentButton'; import { Button } from '~/components/ui/button'; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '~/components/ui/sheet'; @@ -16,13 +17,14 @@ import { getTopPositionedToast } from '~/lib/toast-utils'; export function PublicFooterPrepaidMode(props: { currencyId: CurrencyId; locationId: LocationId }) { const currency = CURRENCIES[props.currencyId]; const [order, setOrder] = useAtom(orderAtom); + const [menuItems] = useAtom(menuItemsAtom); const [clientSecret, setClientSecret] = useState(null); const [isLoading, setIsLoading] = useState(false); const [isCheckingOut, setIsCheckingOut] = useState(false); const [merchantStripeAccountId, setMerchantStripeAccountId] = useState(null); const { toast } = useToast(); - const totalAmount = order.items.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0'), 0); + const totalAmount = 0; // TODO order.items.reduce((sum, item) => sum + parseFloat(item.menuItem?.price ?? '0'), 0); const handleCheckout = async () => { setIsLoading(true); @@ -70,7 +72,10 @@ export function PublicFooterPrepaidMode(props: { currencyId: CurrencyId; locatio {order.items.length > 0 && ( - {order.items.reduce((sum, item) => sum + Number(item.menuItem.price), 0)} + {order.items.reduce((sum, item) => { + const { price } = menuItems[item.menuItemId]!; + return sum + Number(price); + }, 0)} )} @@ -86,19 +91,22 @@ export function PublicFooterPrepaidMode(props: { currencyId: CurrencyId; locatio ) : ( <>
- {order.items.map((item) => ( -
-
-

{item.menuItem.name}

-

- {item.menuItem.price} {currency.symbol} -

+ {order.items.map((item) => { + const { name, price } = menuItems[item.menuItemId]!; + return ( +
+
+

{name}

+

+ {price} {currency.symbol} +

+
+
+ 1 +
-
- 1 -
-
- ))} + ); + })}
diff --git a/src/app/p/[locationSlug]/_state/menu-items-atom.ts b/src/app/p/[locationSlug]/_state/menu-items-atom.ts new file mode 100644 index 0000000..54623b0 --- /dev/null +++ b/src/app/p/[locationSlug]/_state/menu-items-atom.ts @@ -0,0 +1,5 @@ +import { atom } from 'jotai'; +import type { MenuItem } from '~/domain/menu-items'; + +export const menuItemsAtom = atom([]); +menuItemsAtom.debugLabel = 'menuItemsAtom'; diff --git a/src/app/p/[locationSlug]/_state/cart.ts b/src/app/p/[locationSlug]/_state/order-atom.ts similarity index 100% rename from src/app/p/[locationSlug]/_state/cart.ts rename to src/app/p/[locationSlug]/_state/order-atom.ts diff --git a/src/app/p/[locationSlug]/layout.tsx b/src/app/p/[locationSlug]/layout.tsx index 541ce88..71d6437 100644 --- a/src/app/p/[locationSlug]/layout.tsx +++ b/src/app/p/[locationSlug]/layout.tsx @@ -8,6 +8,7 @@ import { CookieKey } from '~/domain/cookies'; import { locationSlugSchema } from '~/domain/locations'; import { AppError } from '~/lib/error-utils.server'; import { getLocationPublicDataBySlug } from '~/server/queries/locations'; +import { getMenuItemsByLocation } from '~/server/queries/menu-items'; import { capturePublicLocationVisit } from './_actions/captureAnalytics'; //TODO Use cache @@ -26,13 +27,16 @@ export default async function Layout({ params, children }: { params: Params; chi const parsedLocationSlug = locationSlugValidationResult.data; const location = await getLocationPublicDataBySlug(parsedLocationSlug); + + const menuItems = await getMenuItemsByLocation(location.id); + const cookieStore = cookies(); const machineId = (await cookieStore).get(CookieKey.MachineId)?.value; await capturePublicLocationVisit(machineId, location.orgId, parsedLocationSlug); return ( - +
diff --git a/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts b/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts index b71711c..b2871f5 100644 --- a/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts +++ b/src/app/u/[locationId]/live/_actions/markOrderItemAsDeliveredAction.ts @@ -48,11 +48,7 @@ export async function markOrderItemAsDeliveredAction(locationId: LocationId, ord await notifyOrderUpdated(locationId, { ...order, items: order.items.map((item) => ({ - menuItem: { - id: item.menuItem.id, - name: item.menuItem.name ?? '', - price: item.menuItem.price, - }, + menuItemId: item.menuItemId, orderItem: { id: item.orderItem.id, isDelivered: item.orderItem.isDelivered, diff --git a/src/app/u/[locationId]/live/_components/OpenOrdersList.tsx b/src/app/u/[locationId]/live/_components/OpenOrdersList.tsx index 0f794e6..eaa265a 100644 --- a/src/app/u/[locationId]/live/_components/OpenOrdersList.tsx +++ b/src/app/u/[locationId]/live/_components/OpenOrdersList.tsx @@ -20,7 +20,7 @@ export async function OpenOrdersList(props: { locationId: LocationId }) { }); } - const menuItemIds = openOrders.flatMap((order) => order.items.map((item) => item.menuItem.id)); + const menuItemIds = openOrders.flatMap((order) => order.items.map((item) => item.menuItemId)); const menuItemsData = (await db.query.menuItems.findMany({ where: (menuItems, { inArray }) => inArray(menuItems.id, menuItemIds), columns: { diff --git a/src/app/u/[locationId]/live/_components/OrderCard.tsx b/src/app/u/[locationId]/live/_components/OrderCard.tsx index fb73da0..e6053cb 100644 --- a/src/app/u/[locationId]/live/_components/OrderCard.tsx +++ b/src/app/u/[locationId]/live/_components/OrderCard.tsx @@ -44,15 +44,15 @@ export function OrderCard({
{order.items.map((item, index) => (

- [{item.orderItem.id}]{menuItemsMap.get(item.menuItem.id)?.name ?? 'Unknown Item'} + [{item.orderItem.id}]{menuItemsMap.get(item.menuItemId)?.name ?? 'Unknown Item'}

- ${menuItemsMap.get(item.menuItem.id)?.price ?? 'Unknown Item'} + ${menuItemsMap.get(item.menuItemId)?.price ?? 'Unknown Item'}

diff --git a/src/components/public/PublicMenuItem.tsx b/src/components/public/PublicMenuItem.tsx index 181bfc2..9cec46e 100644 --- a/src/components/public/PublicMenuItem.tsx +++ b/src/components/public/PublicMenuItem.tsx @@ -2,7 +2,7 @@ import { useAtom } from 'jotai'; import { PlusIcon, SoupIcon, WineIcon } from 'lucide-react'; -import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; +import { orderAtom } from '~/app/p/[locationSlug]/_state/order-atom'; import { Badge } from '~/components/ui/badge'; import { CURRENCIES, type CurrencyId } from '~/domain/currencies'; import { type MenuItem } from '~/domain/menu-items'; @@ -17,13 +17,13 @@ export function PublicMenuItem(props: { item: MenuItem; currencyId: CurrencyId; const addToOrder = () => { setOrder((prevOrder) => { - const { id, name, price, type } = props.item; + const { id } = props.item; return { ...prevOrder, items: [ ...prevOrder.items, { - menuItem: { id, name, price, type }, + menuItemId: id, orderItem: { isDelivered: false, isPaid: false }, }, ], diff --git a/src/domain/order-items.ts b/src/domain/order-items.ts index b2913f8..2b5399b 100644 --- a/src/domain/order-items.ts +++ b/src/domain/order-items.ts @@ -3,7 +3,7 @@ // export const ORDER_ITEM_STATUSES = [...new Set([...POSTPAID_STATUSES, ...PREPAID_STATUSES])] as const; import type { InferSelectModel } from 'drizzle-orm'; -import { MenuItem } from '~/domain/menu-items'; +import { MenuItem, MenuItemId } from '~/domain/menu-items'; import { orderItems } from '~/server/db/schema'; // type PostpaidOrderItemStatus = (typeof POSTPAID_STATUSES)[number]; @@ -14,6 +14,6 @@ import { orderItems } from '~/server/db/schema'; export type OrderItem = InferSelectModel; export interface PublicOrderItem { - menuItem: Pick; + menuItemId: MenuItemId; orderItem: { id?: OrderItem['id'] } & Pick; } diff --git a/src/hooks/use-real-time.ts b/src/hooks/use-real-time.ts index 8db50f4..07574b0 100644 --- a/src/hooks/use-real-time.ts +++ b/src/hooks/use-real-time.ts @@ -1,6 +1,6 @@ import { useAtom } from 'jotai'; import { useEffect } from 'react'; -import { orderAtom } from '~/app/p/[locationSlug]/_state/cart'; +import { orderAtom } from '~/app/p/[locationSlug]/_state/order-atom'; import { PublicOrder, type PublicOrderWithItems } from '~/domain/orders'; import { useToast } from '~/hooks/use-toast'; import { CHANNELS, EVENTS, pusherClient } from '~/lib/pusher'; diff --git a/src/server/queries/orders.ts b/src/server/queries/orders.ts index ef126f0..1c4ded2 100644 --- a/src/server/queries/orders.ts +++ b/src/server/queries/orders.ts @@ -39,7 +39,7 @@ export async function createOrder(data: z.infer): Promis .insert(orderItems) .values({ orderId: order.id, - menuItemId: item!.menuItem.id, + menuItemId: item!.menuItemId, isDelivered: false, isPaid: false, createdAt: sql`CURRENT_TIMESTAMP`, @@ -48,11 +48,7 @@ export async function createOrder(data: z.infer): Promis .returning(); if (insertedItem) { const insertedPublicItem: PublicOrderItem = { - menuItem: { - id: item!.menuItem.id, - name: item!.menuItem.name, - price: item!.menuItem.price, - }, + menuItemId: item!.menuItemId, orderItem: { id: insertedItem.id, isDelivered: false, @@ -83,7 +79,7 @@ export async function updateOrder(data: z.infer): Promis .insert(orderItems) .values({ orderId: Number(data.orderId), //TODO review - menuItemId: item!.menuItem.id, + menuItemId: item!.menuItemId, isDelivered: false, isPaid: false, createdAt: sql`CURRENT_TIMESTAMP`, @@ -92,11 +88,7 @@ export async function updateOrder(data: z.infer): Promis .returning(); if (insertedItem) { const insertedPublicItem: PublicOrderItem = { - menuItem: { - id: item!.menuItem.id, - name: item!.menuItem.name, - price: item!.menuItem.price, - }, + menuItemId: item!.menuItemId, orderItem: { id: insertedItem.id, isDelivered: false, @@ -146,11 +138,7 @@ export const getOpenOrdersByLocation = async (locationId: LocationId): Promise

({ - menuItem: { - id: orderItem.menuItemId, - name: '', // TODO Placeholder, replace with actual value if available - price: '0', // TODO Placeholder, replace with actual value if available - }, + menuItemId: orderItem.menuItemId, orderItem: { id: orderItem.id, isDelivered: orderItem.isDelivered, @@ -192,12 +180,7 @@ export async function getOrderById(locationId: LocationId, orderId: OrderId): Pr createdAt: order.createdAt, updatedAt: order.updatedAt, items: order.orderItems.map((orderItem) => ({ - menuItem: { - id: orderItem.menuItemId, - // You may need to fetch menuItem name and price here if not included in orderItem - name: '', // TODO Placeholder, replace with actual value if available - price: '0', // TODO Placeholder, replace with actual value if available - }, + menuItemId: orderItem.menuItemId, orderItem: { id: orderItem.id, isDelivered: orderItem.isDelivered, From fb000450dd90b9c6153cbe6f2aff0cb26b5da44c Mon Sep 17 00:00:00 2001 From: dopoto Date: Mon, 19 May 2025 13:52:57 +0300 Subject: [PATCH 34/47] fix --- .../p/[locationSlug]/_components/JotaiProviderWrapper.tsx | 2 +- src/app/p/[locationSlug]/_components/OrderItemsList.tsx | 2 +- .../[locationSlug]/_components/PublicFooterPostpaidMode.tsx | 2 ++ src/app/p/[locationSlug]/_state/menu-items-atom.ts | 5 +++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx b/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx index 4ad6f91..0c79fd6 100644 --- a/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx +++ b/src/app/p/[locationSlug]/_components/JotaiProviderWrapper.tsx @@ -28,7 +28,7 @@ function Initializer(props: { locationId: LocationId; currencyId: CurrencyId; me }, [props.locationId, setOrder]); useEffect(() => { - setMenuItems(props.menuItems); + setMenuItems(new Map(props.menuItems.map((item) => [item.id, item]))); }, [props.menuItems, setMenuItems]); return null; diff --git a/src/app/p/[locationSlug]/_components/OrderItemsList.tsx b/src/app/p/[locationSlug]/_components/OrderItemsList.tsx index c9c922b..d04b2b3 100644 --- a/src/app/p/[locationSlug]/_components/OrderItemsList.tsx +++ b/src/app/p/[locationSlug]/_components/OrderItemsList.tsx @@ -12,7 +12,7 @@ export function OrderItemsList(props: { items: PublicOrderItem[] }) { return ( <> {props.items?.map((item, index) => { - const menuItem = menuItems[item.menuItemId] ?? { name: 'Unknown item', price: 0 }; + const menuItem = menuItems.get(item.menuItemId) ?? { name: 'Unknown item', price: 0 }; const { name, price } = menuItem; return (

diff --git a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx index 23bfac3..43f9769 100644 --- a/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx +++ b/src/app/p/[locationSlug]/_components/PublicFooterPostpaidMode.tsx @@ -119,6 +119,8 @@ export function PublicFooterPostpaidMode(props: { currencyId: CurrencyId; locati } }; + console.log('DBGA' + JSON.stringify(order.items, null, 2)); + const draftItems = order.items.filter((item) => !item.orderItem.id); const inPreparationItems = order.items.filter((item) => item.orderItem.id && item.orderItem.isDelivered === false); const deliveredItems = order.items.filter((item) => item.orderItem.id && item.orderItem.isDelivered === true); diff --git a/src/app/p/[locationSlug]/_state/menu-items-atom.ts b/src/app/p/[locationSlug]/_state/menu-items-atom.ts index 54623b0..85137b1 100644 --- a/src/app/p/[locationSlug]/_state/menu-items-atom.ts +++ b/src/app/p/[locationSlug]/_state/menu-items-atom.ts @@ -1,5 +1,6 @@ +import type { InferSelectModel } from 'drizzle-orm'; import { atom } from 'jotai'; -import type { MenuItem } from '~/domain/menu-items'; +import { menuItems } from '~/server/db/schema'; -export const menuItemsAtom = atom([]); +export const menuItemsAtom = atom>>(new Map()); menuItemsAtom.debugLabel = 'menuItemsAtom'; From 827485e3b70bf4979837fa8d4f26a11a41088878 Mon Sep 17 00:00:00 2001 From: dopoto Date: Thu, 22 May 2025 09:18:11 +0300 Subject: [PATCH 35/47] three state --- .../live/_components/OrderCard.tsx | 9 +- src/components/ThreeStateToggle.tsx | 202 ++++++++++++++++++ 2 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 src/components/ThreeStateToggle.tsx diff --git a/src/app/u/[locationId]/live/_components/OrderCard.tsx b/src/app/u/[locationId]/live/_components/OrderCard.tsx index e6053cb..c7b8c1a 100644 --- a/src/app/u/[locationId]/live/_components/OrderCard.tsx +++ b/src/app/u/[locationId]/live/_components/OrderCard.tsx @@ -1,8 +1,9 @@ 'use client'; import type { InferSelectModel } from 'drizzle-orm'; -import { Check, Clock } from 'lucide-react'; +import { BanIcon, Check, CircleCheckIcon, Clock, ClockIcon } from 'lucide-react'; import { useState } from 'react'; +import { ThreeStateToggle } from '~/components/ThreeStateToggle'; import { Button } from '~/components/ui/button'; import { Card } from '~/components/ui/card'; import { type LocationId } from '~/domain/locations'; @@ -56,6 +57,12 @@ export function OrderCard({

+ } + centerIcon={} + rightIcon={} + /> {item.orderItem.isDelivered === false && ( <> diff --git a/src/components/ThreeStateToggle.tsx b/src/components/ThreeStateToggle.tsx new file mode 100644 index 0000000..602e984 --- /dev/null +++ b/src/components/ThreeStateToggle.tsx @@ -0,0 +1,202 @@ +'use client'; + +import type React from 'react'; + +import { ChevronLeft, ChevronRight, Minus } from 'lucide-react'; +import { cloneElement, isValidElement, useEffect, useRef, useState } from 'react'; +import { cn } from '~/lib/utils'; + +interface ThreeStateToggleProps { + leftIcon?: React.ReactNode; + centerIcon?: React.ReactNode; + rightIcon?: React.ReactNode; + onStateChange?: (state: number) => void; + defaultState?: 0 | 1 | 2; + className?: string; + size?: number; +} + +export function ThreeStateToggle({ + leftIcon = , + centerIcon = , + rightIcon = , + onStateChange, + defaultState = 2, + className, + size = 48, +}: ThreeStateToggleProps) { + const [selectedState, setSelectedState] = useState(defaultState); + const toggleRef = useRef(null); + const startXRef = useRef(null); + + // Calculate proportional sizes + const padding = Math.max(4, Math.round(size * 0.08)); + const iconSize = Math.max(16, Math.round(size * 0.4)); + + const handleStateChange = (newState: number) => { + if (newState >= 0 && newState <= 2) { + setSelectedState(newState); + onStateChange?.(newState); + } + }; + + const handleTouchStart = (e: React.TouchEvent) => { + if (e.touches && e.touches[0]) { + startXRef.current = e.touches[0].clientX; + } + }; + + const handleTouchEnd = (e: React.TouchEvent) => { + if (startXRef.current === null || e.changedTouches.length === 0) return; + + const touch = e.changedTouches[0]; + if (!touch) return; + const endX = touch.clientX; + const diffX = endX - startXRef.current; + + // Determine swipe direction if the swipe is significant enough + if (Math.abs(diffX) > 30) { + if (diffX > 0) { + // Swipe right + handleStateChange(Math.min(selectedState + 1, 2)); + } else { + // Swipe left + handleStateChange(Math.max(selectedState - 1, 0)); + } + } + + startXRef.current = null; + }; + + const handleMouseDown = (e: React.MouseEvent) => { + startXRef.current = e.clientX; + }; + + const handleMouseUp = (e: React.MouseEvent) => { + if (startXRef.current === null) return; + + const diffX = e.clientX - startXRef.current; + + // Determine swipe direction if the swipe is significant enough + if (Math.abs(diffX) > 30) { + if (diffX > 0) { + // Swipe right + handleStateChange(Math.min(selectedState + 1, 2)); + } else { + // Swipe left + handleStateChange(Math.max(selectedState - 1, 0)); + } + } + + startXRef.current = null; + }; + + // Handle keyboard navigation + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (document.activeElement === toggleRef.current) { + if (e.key === 'ArrowLeft') { + handleStateChange(Math.max(selectedState - 1, 0)); + e.preventDefault(); + } else if (e.key === 'ArrowRight') { + handleStateChange(Math.min(selectedState + 1, 2)); + e.preventDefault(); + } + } + }; + + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [selectedState]); + + // Helper function to resize icons + const resizeIcon = (icon: React.ReactNode) => { + if (isValidElement(icon)) { + const iconProps: Record = {}; + // Only add className if the element supports it + if (icon.props && typeof icon.props === 'object' && 'className' in icon.props) { + const className = + typeof icon.props === 'object' && icon.props !== null && 'className' in icon.props + ? (icon.props.className as string)?.replace(/w-\d+|h-\d+/g, '') || '' + : ''; + iconProps.className = cn(className, `h-${Math.round(iconSize / 4)} w-${Math.round(iconSize / 4)}`); + } + // Only add size if the element supports it + if (icon.props && typeof icon.props === 'object' && 'size' in icon.props) { + iconProps.size = iconSize; + } + return cloneElement(icon as React.ReactElement, iconProps); + } + return icon; + }; + + return ( +
startXRef.current !== null && handleMouseUp(e as any)} + tabIndex={0} + role="slider" + aria-valuemin={0} + aria-valuemax={2} + aria-valuenow={selectedState} + aria-valuetext={['Left', 'Center', 'Right'][selectedState]} + > + {/* Sliding indicator */} +
+ + {/* Left state */} +
handleStateChange(0)} + > + {resizeIcon(leftIcon)} + Left state +
+ + {/* Center state */} +
handleStateChange(1)} + > + {resizeIcon(centerIcon)} + Center state +
+ + {/* Right state */} +
handleStateChange(2)} + > + {resizeIcon(rightIcon)} + Right state +
+
+ ); +} From aa7d621dc797428c835d1999696c82cf19594b6a Mon Sep 17 00:00:00 2001 From: dopoto Date: Thu, 22 May 2025 09:42:18 +0300 Subject: [PATCH 36/47] ts --- src/app/actions/createCartPaymentIntent.ts | 2 +- .../actions/createMenuItemPaymentIntent.ts | 2 +- src/app/actions/placeOrderAction.ts | 4 +-- src/app/actions/updateOrderAction.ts | 4 +-- src/app/api/webhooks/stripe/route.ts | 8 ++--- src/app/layout.tsx | 5 ++- .../_actions/captureAnalytics.ts | 2 +- .../_components/JotaiProviderWrapper.tsx | 6 ++-- .../_components/OrderItemsList.tsx | 2 +- .../_components/PublicFooterPostpaidMode.tsx | 2 +- .../_components/PublicFooterPrepaidMode.tsx | 8 +++-- .../[locationSlug]/_components/PublicMenu.tsx | 2 +- .../_components/PublicMenus.tsx | 2 +- .../[locationSlug]/_state/menu-items-atom.ts | 2 +- src/app/p/[locationSlug]/_state/order-atom.ts | 2 +- .../live/_components/LiveOrders.tsx | 2 +- .../live/_components/OrderCard.tsx | 2 +- .../u/[locationId]/menu-items/add/page.tsx | 2 +- .../menu-items/edit/[menuItemId]/page.tsx | 2 +- src/app/u/[locationId]/menu-items/page.tsx | 2 +- src/app/u/[locationId]/menus/add/page.tsx | 2 +- .../[locationId]/menus/edit/[menuId]/page.tsx | 2 +- src/components/ThreeStateToggle.tsx | 34 +++++++++++-------- src/components/public/PaymentButton.tsx | 2 +- src/components/public/PublicMenuItem.tsx | 2 +- src/domain/locations.ts | 2 +- src/domain/order-items.ts | 4 +-- src/domain/orders.ts | 6 ++-- src/hooks/use-real-time.ts | 4 +-- src/server/db/schema.ts | 4 +-- src/server/queries/orders.ts | 6 ++-- 31 files changed, 69 insertions(+), 62 deletions(-) diff --git a/src/app/actions/createCartPaymentIntent.ts b/src/app/actions/createCartPaymentIntent.ts index edaddb2..ea7038c 100644 --- a/src/app/actions/createCartPaymentIntent.ts +++ b/src/app/actions/createCartPaymentIntent.ts @@ -1,6 +1,6 @@ 'use server'; -import { PublicOrderItem } from '~/domain/order-items'; +import { type PublicOrderItem } from '~/domain/order-items'; import { type PaymentIntentResponse } from '~/domain/payments'; import { createPaymentIntent, verifyConnectAccount } from '~/lib/payment-utils'; import { getMenuItemsByLocation } from '~/server/queries/menu-items'; diff --git a/src/app/actions/createMenuItemPaymentIntent.ts b/src/app/actions/createMenuItemPaymentIntent.ts index 137b822..0017d22 100644 --- a/src/app/actions/createMenuItemPaymentIntent.ts +++ b/src/app/actions/createMenuItemPaymentIntent.ts @@ -8,7 +8,7 @@ export async function createMenuItemPaymentIntent( merchantStripeAccountId: string, ): Promise { try { - if (!merchantStripeAccountId || !merchantStripeAccountId.startsWith('acct_')) { + if (!merchantStripeAccountId?.startsWith('acct_')) { //TODO throw new Error('Invalid merchant Stripe account'); } diff --git a/src/app/actions/placeOrderAction.ts b/src/app/actions/placeOrderAction.ts index ac242a8..79f4dd8 100644 --- a/src/app/actions/placeOrderAction.ts +++ b/src/app/actions/placeOrderAction.ts @@ -2,9 +2,9 @@ import * as Sentry from '@sentry/nextjs'; import { headers } from 'next/headers'; -import { z } from 'zod'; +import { type z } from 'zod'; import { notifyOrderCreated } from '~/app/api/realtime/notifications'; -import { menuFormSchema } from '~/domain/menus'; +import { type menuFormSchema } from '~/domain/menus'; import { orderFormSchema, type PublicOrderWithItems } from '~/domain/orders'; import { AppError } from '~/lib/error-utils.server'; import { type FormState, processFormErrors } from '~/lib/form-state'; diff --git a/src/app/actions/updateOrderAction.ts b/src/app/actions/updateOrderAction.ts index 3337601..2218e69 100644 --- a/src/app/actions/updateOrderAction.ts +++ b/src/app/actions/updateOrderAction.ts @@ -2,9 +2,9 @@ import * as Sentry from '@sentry/nextjs'; import { headers } from 'next/headers'; -import { z } from 'zod'; +import { type z } from 'zod'; import { notifyOrderUpdated } from '~/app/api/realtime/notifications'; -import { menuFormSchema } from '~/domain/menus'; +import { type menuFormSchema } from '~/domain/menus'; import { orderFormSchema, type PublicOrderWithItems } from '~/domain/orders'; import { AppError } from '~/lib/error-utils.server'; import { type FormState, processFormErrors } from '~/lib/form-state'; diff --git a/src/app/api/webhooks/stripe/route.ts b/src/app/api/webhooks/stripe/route.ts index 05edfca..edaabf2 100644 --- a/src/app/api/webhooks/stripe/route.ts +++ b/src/app/api/webhooks/stripe/route.ts @@ -36,16 +36,16 @@ export async function POST(request: Request) { try { switch (event.type) { case 'payment_intent.succeeded': - await handlePaymentIntentSucceeded(event.data.object as Stripe.PaymentIntent); + await handlePaymentIntentSucceeded(event.data.object); break; case 'payment_intent.payment_failed': - await handlePaymentIntentFailed(event.data.object as Stripe.PaymentIntent); + await handlePaymentIntentFailed(event.data.object); break; case 'charge.refunded': - await handleChargeRefunded(event.data.object as Stripe.Charge); + await handleChargeRefunded(event.data.object); break; case 'payment_intent.requires_action': - await handlePaymentIntentRequiresAction(event.data.object as Stripe.PaymentIntent); + await handlePaymentIntentRequiresAction(event.data.object); break; default: console.log(`Unhandled event type: ${event.type}`); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 5f26f5d..9041144 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -15,14 +15,13 @@ export const metadata: Metadata = { icons: [{ rel: 'icon', url: '/favicon.ico' }], }; -export default function RootLayout(props: { children: ReactNode; modal: ReactNode }) { +export default function RootLayout({ children }: { children: ReactNode }) { return ( -
{props.children}
- {props.modal} +
{children}