Open Source Team Metrics based on PRs

botid

Changed files
+333 -31
app
api
categories
[categoryId]
dashboard
data
github
organizations
[orgName]
repositories
repositories
sync
user
github-app
installations
[installationId]
organizations
[orgId]
ai-settings
members
dashboard
lifecycle
components
lib
+13
app/api/categories/[categoryId]/route.ts
··· 9 9 import { getOrganizationRole } from '@/lib/repositories/user-repository'; 10 10 import { Category } from '@/lib/types'; 11 11 import { z } from 'zod'; 12 + import { verifyBotId } from '@/lib/botid-verification'; 12 13 13 14 export const runtime = 'nodejs'; 14 15 ··· 23 24 { params }: { params: Promise<{ categoryId: string }> } 24 25 ) { 25 26 try { 27 + // Check for bot before proceeding 28 + const botVerification = await verifyBotId(); 29 + if (botVerification) { 30 + return botVerification; 31 + } 32 + 26 33 const { categoryId } = await params; 27 34 const session = await auth(); 28 35 if (!session?.user?.id) { ··· 123 130 { params }: { params: Promise<{ categoryId: string }> } 124 131 ) { 125 132 try { 133 + // Check for bot before proceeding 134 + const botVerification = await verifyBotId(); 135 + if (botVerification) { 136 + return botVerification; 137 + } 138 + 126 139 const { categoryId } = await params; 127 140 const session = await auth(); 128 141 if (!session?.user?.id) {
+7
app/api/dashboard/data/route.ts
··· 1 1 import { NextRequest, NextResponse } from 'next/server'; 2 2 import { getUserWithOrganizations } from '@/lib/auth-context'; 3 + import { verifyBotId } from '@/lib/botid-verification'; 3 4 4 5 export const runtime = 'nodejs'; 5 6 6 7 export async function GET(request: NextRequest) { 7 8 try { 9 + // Check for bot before proceeding 10 + const botVerification = await verifyBotId(); 11 + if (botVerification) { 12 + return botVerification; 13 + } 14 + 8 15 const { searchParams } = new URL(request.url); 9 16 const include = searchParams.get('include')?.split(',') || []; 10 17
+19
app/api/github-app/installations/[installationId]/route.ts
··· 7 7 import { auth } from '@/auth' 8 8 import { getService } from '@/lib/core/container/di-container' 9 9 import { IGitHubAppService } from '@/lib/core/ports' 10 + import { verifyBotId } from '@/lib/botid-verification' 10 11 11 12 export const runtime = 'nodejs' 12 13 ··· 19 20 { params }: { params: Promise<{ installationId: string }> } 20 21 ) { 21 22 try { 23 + // Check for bot before proceeding 24 + const botVerification = await verifyBotId(); 25 + if (botVerification) { 26 + return botVerification; 27 + } 28 + 22 29 const { installationId } = await params 23 30 const session = await auth() 24 31 ··· 84 91 { params }: { params: Promise<{ installationId: string }> } 85 92 ) { 86 93 try { 94 + // Check for bot before proceeding 95 + const botVerification = await verifyBotId(); 96 + if (botVerification) { 97 + return botVerification; 98 + } 99 + 87 100 const { installationId } = await params 88 101 const session = await auth() 89 102 ··· 150 163 { params }: { params: Promise<{ installationId: string }> } 151 164 ) { 152 165 try { 166 + // Check for bot before proceeding 167 + const botVerification = await verifyBotId(); 168 + if (botVerification) { 169 + return botVerification; 170 + } 171 + 153 172 const { installationId } = await params 154 173 const session = await auth() 155 174
+13
app/api/github-app/installations/route.ts
··· 7 7 import { auth } from '@/auth' 8 8 import { getService } from '@/lib/core/container/di-container' 9 9 import { IGitHubAppService } from '@/lib/core/ports' 10 + import { verifyBotId } from '@/lib/botid-verification' 10 11 11 12 export const runtime = 'nodejs' 12 13 ··· 16 17 */ 17 18 export async function GET() { 18 19 try { 20 + // Check for bot before proceeding 21 + const botVerification = await verifyBotId(); 22 + if (botVerification) { 23 + return botVerification; 24 + } 25 + 19 26 const session = await auth() 20 27 21 28 if (!session || !session.user) { ··· 71 78 */ 72 79 export async function POST() { 73 80 try { 81 + // Check for bot before proceeding 82 + const botVerification = await verifyBotId(); 83 + if (botVerification) { 84 + return botVerification; 85 + } 86 + 74 87 const session = await auth() 75 88 76 89 if (!session || !session.user) {
+7
app/api/github/organizations/[orgName]/repositories/route.ts
··· 2 2 import { auth } from '@/auth'; 3 3 import { getService } from '@/lib/core/container/di-container'; 4 4 import { IGitHubService } from '@/lib/core/ports'; 5 + import { verifyBotId } from '@/lib/botid-verification'; 5 6 6 7 // Use the context object directly with proper typing for Next.js route handler 7 8 export async function GET( 8 9 request: NextRequest, 9 10 { params }: { params: Promise<{ orgName: string }> } 10 11 ) { 12 + // Check for bot before proceeding 13 + const botVerification = await verifyBotId(); 14 + if (botVerification) { 15 + return botVerification; 16 + } 17 + 11 18 const { orgName } = await params; 12 19 13 20 const session = await auth();
+7
app/api/github/organizations/route.ts
··· 2 2 import { auth } from '@/auth'; 3 3 import { getService } from '@/lib/core/container/di-container'; 4 4 import { IGitHubService } from '@/lib/core/ports'; 5 + import { verifyBotId } from '@/lib/botid-verification'; 5 6 6 7 export async function GET() { 8 + // Check for bot before proceeding 9 + const botVerification = await verifyBotId(); 10 + if (botVerification) { 11 + return botVerification; 12 + } 13 + 7 14 const session = await auth(); 8 15 9 16 if (!session || !session.user) {
+7
app/api/github/repositories/sync/route.ts
··· 2 2 import { auth } from '@/auth'; 3 3 import { GitHubService } from '@/lib/services'; 4 4 import { findUserById, findUserByEmail } from '@/lib/repositories'; 5 + import { verifyBotId } from '@/lib/botid-verification'; 5 6 6 7 export const runtime = 'nodejs'; 7 8 8 9 export async function POST() { 10 + // Check for bot before proceeding 11 + const botVerification = await verifyBotId(); 12 + if (botVerification) { 13 + return botVerification; 14 + } 15 + 9 16 const session = await auth(); 10 17 11 18 if (!session || !session.user) {
+7
app/api/github/user/route.ts
··· 2 2 import { auth } from '@/auth'; 3 3 import { getService } from '@/lib/core/container/di-container'; 4 4 import { IGitHubService } from '@/lib/core/ports'; 5 + import { verifyBotId } from '@/lib/botid-verification'; 5 6 6 7 export async function GET() { 8 + // Check for bot before proceeding 9 + const botVerification = await verifyBotId(); 10 + if (botVerification) { 11 + return botVerification; 12 + } 13 + 7 14 const session = await auth(); 8 15 9 16 if (!session || !session.user) {
+13
app/api/organizations/[orgId]/ai-settings/route.ts
··· 3 3 import { AiSettingsService } from '@/lib/services/ai-settings-service'; 4 4 import { z } from 'zod'; 5 5 import { unauthorized, badRequest, errorResponse } from '@/lib/api-errors'; 6 + import { verifyBotId } from '@/lib/botid-verification'; 6 7 7 8 const updateAiSettingsSchema = z.object({ 8 9 provider: z.enum(['openai', 'google', 'anthropic']).nullable().optional(), ··· 19 20 { params }: { params: Promise<{ orgId: string }> } 20 21 ) { 21 22 try { 23 + // Check for bot before proceeding 24 + const botVerification = await verifyBotId(); 25 + if (botVerification) { 26 + return botVerification; 27 + } 28 + 22 29 const { orgId } = await params; 23 30 const session = await auth(); 24 31 if (!session?.user?.id) throw unauthorized(); ··· 39 46 { params }: { params: Promise<{ orgId: string }> } 40 47 ) { 41 48 try { 49 + // Check for bot before proceeding 50 + const botVerification = await verifyBotId(); 51 + if (botVerification) { 52 + return botVerification; 53 + } 54 + 42 55 const { orgId } = await params; 43 56 const session = await auth(); 44 57 if (!session?.user?.id) throw unauthorized();
+7
app/api/organizations/[orgId]/members/route.ts
··· 2 2 import { auth } from '@/auth'; 3 3 import { getOrganizationMembers, searchUsers } from '@/lib/repositories/team-repository'; 4 4 import { getOrganizationRole } from '@/lib/repositories/user-repository'; 5 + import { verifyBotId } from '@/lib/botid-verification'; 5 6 6 7 export const runtime = 'nodejs'; 7 8 ··· 11 12 { params }: { params: Promise<{ orgId: string }> } 12 13 ) { 13 14 try { 15 + // Check for bot before proceeding 16 + const botVerification = await verifyBotId(); 17 + if (botVerification) { 18 + return botVerification; 19 + } 20 + 14 21 const { orgId } = await params; 15 22 const session = await auth(); 16 23 if (!session?.user?.id) {
+10 -16
app/dashboard/lifecycle/page.tsx
··· 1 1 "use client"; 2 2 3 3 import { useState, useEffect } from "react"; 4 - import { DateRange } from "react-day-picker"; 5 4 import { AppSidebar } from "@/components/app-sidebar" 6 5 import { ChartAreaEngineering } from "@/components/chart-area-engineering" 7 - import { DateRangePickerWithPresets } from "@/components/date-range-picker" 6 + import { SimpleTimeRangePicker, type TimeRangeValue } from "@/components/simple-time-range-picker" 8 7 import { PRQualityDetails } from "@/components/pr-quality-details" 9 8 import { RepositoryFilter } from "@/components/repository-filter" 10 9 import { SiteHeader } from "@/components/site-header" ··· 16 15 17 16 export default function LifecyclePage() { 18 17 const [selectedRepository, setSelectedRepository] = useState<string>("all"); 19 - const [dateRange, setDateRange] = useState<DateRange | undefined>(); 18 + const [timeRange, setTimeRange] = useState<TimeRangeValue>("30d"); 20 19 const [chartData, setChartData] = useState<TimeSeriesDataPoint[]>([]); 21 20 const [loading, setLoading] = useState(true); 22 21 const [error, setError] = useState<string | null>(null); ··· 35 34 params.append("repositoryId", selectedRepository); 36 35 } 37 36 38 - if (dateRange?.from) { 39 - params.append("startDate", dateRange.from.toISOString()); 40 - } 41 - 42 - if (dateRange?.to) { 43 - params.append("endDate", dateRange.to.toISOString()); 44 - } 37 + // Use timeRange instead of specific dates 38 + params.append("timeRange", timeRange); 45 39 46 40 const url = `/api/metrics/time-series${params.toString() ? `?${params.toString()}` : ''}`; 47 41 const response = await fetch(url); ··· 61 55 }; 62 56 63 57 fetchChartData(); 64 - }, [selectedRepository, dateRange]); 58 + }, [selectedRepository, timeRange]); 65 59 66 60 const handleRepositoryChange = (repositoryId: string) => { 67 61 setSelectedRepository(repositoryId); 68 62 }; 69 63 70 - const handleDateRangeChange = (range: DateRange | undefined) => { 71 - setDateRange(range); 64 + const handleTimeRangeChange = (range: TimeRangeValue) => { 65 + setTimeRange(range); 72 66 }; 73 67 74 68 return ( ··· 92 86 onRepositoryChange={handleRepositoryChange} 93 87 selectedRepository={selectedRepository} 94 88 /> 95 - <DateRangePickerWithPresets 96 - onDateRangeChange={handleDateRangeChange} 97 - selectedRange={dateRange} 89 + <SimpleTimeRangePicker 90 + value={timeRange} 91 + onValueChange={handleTimeRangeChange} 98 92 /> 99 93 </div> 100 94 </div>
+61
components/simple-time-range-picker.tsx
··· 1 + "use client" 2 + 3 + import { CalendarIcon } from "lucide-react" 4 + import { Button } from "@/components/ui/button" 5 + import { 6 + Select, 7 + SelectContent, 8 + SelectItem, 9 + SelectTrigger, 10 + SelectValue, 11 + } from "@/components/ui/select" 12 + 13 + export type TimeRangeValue = "7d" | "14d" | "30d" | "90d" 14 + 15 + interface TimeRangeOption { 16 + value: TimeRangeValue; 17 + label: string; 18 + description: string; 19 + } 20 + 21 + interface SimpleTimeRangePickerProps { 22 + value?: TimeRangeValue; 23 + onValueChange: (value: TimeRangeValue) => void; 24 + className?: string; 25 + } 26 + 27 + const timeRangeOptions: TimeRangeOption[] = [ 28 + { value: "7d", label: "Last 7 days", description: "Past week" }, 29 + { value: "14d", label: "Last 14 days", description: "Past 2 weeks" }, 30 + { value: "30d", label: "Last 30 days", description: "Past month" }, 31 + { value: "90d", label: "Last 90 days", description: "Past quarter" }, 32 + ]; 33 + 34 + export function SimpleTimeRangePicker({ 35 + value = "30d", 36 + onValueChange, 37 + className = "" 38 + }: SimpleTimeRangePickerProps) { 39 + const selectedOption = timeRangeOptions.find(option => option.value === value); 40 + 41 + return ( 42 + <Select value={value} onValueChange={onValueChange}> 43 + <SelectTrigger className={`w-[200px] ${className}`}> 44 + <CalendarIcon className="mr-2 h-4 w-4" /> 45 + <SelectValue> 46 + {selectedOption?.label || "Select time range"} 47 + </SelectValue> 48 + </SelectTrigger> 49 + <SelectContent> 50 + {timeRangeOptions.map((option) => ( 51 + <SelectItem key={option.value} value={option.value}> 52 + <div className="flex flex-col"> 53 + <span className="font-medium">{option.label}</span> 54 + <span className="text-xs text-muted-foreground">{option.description}</span> 55 + </div> 56 + </SelectItem> 57 + ))} 58 + </SelectContent> 59 + </Select> 60 + ) 61 + }
+4 -6
components/ui/calendar.tsx
··· 60 60 ...classNames, 61 61 }} 62 62 components={{ 63 - IconLeft: ({ className, ...props }) => ( 64 - <ChevronLeft className={cn("size-4", className)} {...props} /> 65 - ), 66 - IconRight: ({ className, ...props }) => ( 67 - <ChevronRight className={cn("size-4", className)} {...props} /> 68 - ), 63 + Chevron: ({ orientation, className, ...props }: { orientation?: 'up' | 'right' | 'left' | 'down'; className?: string; size?: number; disabled?: boolean }) => { 64 + const Icon = orientation === 'left' ? ChevronLeft : ChevronRight 65 + return <Icon className={cn("size-4", className)} {...props} /> 66 + }, 69 67 }} 70 68 {...props} 71 69 />
+62
instrumentation-client.ts
··· 1 + import { initBotId } from 'botid/client/core'; 2 + 3 + initBotId({ 4 + protect: [ 5 + { 6 + path: '/api/organizations/*/teams', 7 + method: 'POST', 8 + }, 9 + { 10 + path: '/api/organizations/*/teams/*', 11 + method: 'PUT', 12 + }, 13 + { 14 + path: '/api/organizations/*/teams/*', 15 + method: 'DELETE', 16 + }, 17 + { 18 + path: '/api/organizations/*/teams/*/members', 19 + method: 'POST', 20 + }, 21 + { 22 + path: '/api/organizations/*/teams/*/members', 23 + method: 'PUT', 24 + }, 25 + { 26 + path: '/api/organizations/*/teams/*/members', 27 + method: 'DELETE', 28 + }, 29 + { 30 + path: '/api/organizations/*/ai-settings', 31 + method: 'PUT', 32 + }, 33 + { 34 + path: '/api/organizations/*/categories', 35 + method: 'POST', 36 + }, 37 + { 38 + path: '/api/categories/*', 39 + method: 'PUT', 40 + }, 41 + { 42 + path: '/api/categories/*', 43 + method: 'DELETE', 44 + }, 45 + { 46 + path: '/api/github/organizations/sync', 47 + method: 'POST', 48 + }, 49 + { 50 + path: '/api/github/organizations/*/sync', 51 + method: 'POST', 52 + }, 53 + { 54 + path: '/api/github/repositories/*/webhook', 55 + method: 'POST', 56 + }, 57 + { 58 + path: '/api/github/repositories/*/webhook', 59 + method: 'DELETE', 60 + }, 61 + ], 62 + });
+41
lib/botid-verification.ts
··· 1 + import { checkBotId } from 'botid/server'; 2 + import { NextRequest, NextResponse } from 'next/server'; 3 + 4 + /** 5 + * Utility function to verify botid and return 403 if request is from a bot 6 + * @returns Promise<NextResponse | null> - Returns 403 response if bot detected, null if verification passes 7 + */ 8 + export async function verifyBotId(): Promise<NextResponse | null> { 9 + try { 10 + // Check if the request is from a bot 11 + const verification = await checkBotId(); 12 + 13 + if (verification.isBot) { 14 + return NextResponse.json({ error: 'Access denied' }, { status: 403 }); 15 + } 16 + 17 + return null; // Verification passed 18 + } catch (error) { 19 + console.error('BotId verification error:', error); 20 + // In case of verification error, allow request to proceed to avoid blocking legitimate users 21 + return null; 22 + } 23 + } 24 + 25 + /** 26 + * Higher-order function to wrap API route handlers with botid verification 27 + */ 28 + export function withBotIdVerification<T extends (...args: any[]) => Promise<NextResponse>>( 29 + handler: T 30 + ): T { 31 + return (async (...args: any[]) => { 32 + // Check for bot before executing the handler 33 + const botVerification = await verifyBotId(); 34 + if (botVerification) { 35 + return botVerification; 36 + } 37 + 38 + // If verification passes, execute the original handler 39 + return await handler(...args); 40 + }) as T; 41 + }
+13
lib/core/application/auth-middleware.ts
··· 13 13 createAnonymousContext, 14 14 UserPermissions 15 15 } from './context' 16 + import { verifyBotId } from '../../botid-verification' 16 17 17 18 export type AuthenticatedHandler<TRequest = NextRequest, TResponse = NextResponse> = 18 19 (context: ApplicationContext, request: TRequest) => Promise<TResponse> ··· 28 29 ): (request: TRequest) => Promise<TResponse | NextResponse> { 29 30 return async (request: TRequest) => { 30 31 try { 32 + // Check for bot before proceeding 33 + const botVerification = await verifyBotId(); 34 + if (botVerification) { 35 + return botVerification; 36 + } 37 + 31 38 const context = await createAuthenticatedContext(request) 32 39 33 40 if (!context) { ··· 50 57 ): (request: TRequest) => Promise<TResponse> { 51 58 return async (request: TRequest) => { 52 59 try { 60 + // Check for bot before proceeding 61 + const botVerification = await verifyBotId(); 62 + if (botVerification) { 63 + return botVerification as TResponse; 64 + } 65 + 53 66 const context = await createRequestContext(request) 54 67 return await handler(context, request) 55 68 } catch (error) {
+2 -1
next.config.ts
··· 1 1 import type { NextConfig } from "next"; 2 + import { withBotId } from 'botid/next/config'; 2 3 3 4 const nextConfig: NextConfig = { 4 5 /* config options here */ ··· 14 15 }, 15 16 }; 16 17 17 - export default nextConfig; 18 + export default withBotId(nextConfig);
+2 -1
package.json
··· 47 47 "@tanstack/react-table": "^8.21.3", 48 48 "@types/jsonwebtoken": "^9.0.9", 49 49 "ai": "^4.3.15", 50 + "botid": "^1.5.3", 50 51 "class-variance-authority": "^0.7.1", 51 52 "clsx": "^2.1.1", 52 53 "cmdk": "^1.1.1", ··· 59 60 "next-auth": "5.0.0-beta.29", 60 61 "next-themes": "^0.4.6", 61 62 "react": "19.1.1", 62 - "react-day-picker": "8.10.1", 63 + "react-day-picker": "9.9.0", 63 64 "react-dom": "19.1.1", 64 65 "recharts": "^2.15.3", 65 66 "sonner": "^2.0.3",
+38 -7
pnpm-lock.yaml
··· 106 106 ai: 107 107 specifier: ^4.3.15 108 108 version: 4.3.15(react@19.1.1)(zod@3.24.3) 109 + botid: 110 + specifier: ^1.5.3 111 + version: 1.5.3(next@15.4.6(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1) 109 112 class-variance-authority: 110 113 specifier: ^0.7.1 111 114 version: 0.7.1 ··· 143 146 specifier: 19.1.1 144 147 version: 19.1.1 145 148 react-day-picker: 146 - specifier: 8.10.1 147 - version: 8.10.1(date-fns@4.1.0)(react@19.1.1) 149 + specifier: 9.9.0 150 + version: 9.9.0(react@19.1.1) 148 151 react-dom: 149 152 specifier: 19.1.1 150 153 version: 19.1.1(react@19.1.1) ··· 498 501 '@csstools/css-tokenizer@3.0.4': 499 502 resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} 500 503 engines: {node: '>=18'} 504 + 505 + '@date-fns/tz@1.4.1': 506 + resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==} 501 507 502 508 '@dnd-kit/accessibility@3.1.1': 503 509 resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==} ··· 2350 2356 before-after-hook@3.0.2: 2351 2357 resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==} 2352 2358 2359 + botid@1.5.3: 2360 + resolution: {integrity: sha512-QMU3icRKLEjJIUxtCuMkzAzVc32lg97qTNN66VJnjPMBUB79YT0N7bA5RFdvxsw+iUMAh+uNnB/2KBneVwkxEA==} 2361 + peerDependencies: 2362 + next: '*' 2363 + react: ^18.0.0 || ^19.0.0 2364 + peerDependenciesMeta: 2365 + next: 2366 + optional: true 2367 + react: 2368 + optional: true 2369 + 2353 2370 brace-expansion@1.1.11: 2354 2371 resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 2355 2372 ··· 2556 2573 resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} 2557 2574 engines: {node: '>= 0.4'} 2558 2575 2576 + date-fns-jalali@4.1.0-0: 2577 + resolution: {integrity: sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==} 2578 + 2559 2579 date-fns@4.1.0: 2560 2580 resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} 2561 2581 ··· 4005 4025 queue-microtask@1.2.3: 4006 4026 resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 4007 4027 4008 - react-day-picker@8.10.1: 4009 - resolution: {integrity: sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==} 4028 + react-day-picker@9.9.0: 4029 + resolution: {integrity: sha512-NtkJbuX6cl/VaGNb3sVVhmMA6LSMnL5G3xNL+61IyoZj0mUZFWTg4hmj7PHjIQ8MXN9dHWhUHFoJWG6y60DKSg==} 4030 + engines: {node: '>=18'} 4010 4031 peerDependencies: 4011 - date-fns: ^2.28.0 || ^3.0.0 4012 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 4032 + react: '>=16.8.0' 4013 4033 4014 4034 react-dom@19.1.1: 4015 4035 resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} ··· 4968 4988 '@csstools/css-tokenizer': 3.0.4 4969 4989 4970 4990 '@csstools/css-tokenizer@3.0.4': {} 4991 + 4992 + '@date-fns/tz@1.4.1': {} 4971 4993 4972 4994 '@dnd-kit/accessibility@3.1.1(react@19.1.1)': 4973 4995 dependencies: ··· 6905 6927 6906 6928 before-after-hook@3.0.2: {} 6907 6929 6930 + botid@1.5.3(next@15.4.6(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1): 6931 + optionalDependencies: 6932 + next: 15.4.6(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) 6933 + react: 19.1.1 6934 + 6908 6935 brace-expansion@1.1.11: 6909 6936 dependencies: 6910 6937 balanced-match: 1.0.2 ··· 7108 7135 call-bound: 1.0.4 7109 7136 es-errors: 1.3.0 7110 7137 is-data-view: 1.0.2 7138 + 7139 + date-fns-jalali@4.1.0-0: {} 7111 7140 7112 7141 date-fns@4.1.0: {} 7113 7142 ··· 8840 8869 8841 8870 queue-microtask@1.2.3: {} 8842 8871 8843 - react-day-picker@8.10.1(date-fns@4.1.0)(react@19.1.1): 8872 + react-day-picker@9.9.0(react@19.1.1): 8844 8873 dependencies: 8874 + '@date-fns/tz': 1.4.1 8845 8875 date-fns: 4.1.0 8876 + date-fns-jalali: 4.1.0-0 8846 8877 react: 19.1.1 8847 8878 8848 8879 react-dom@19.1.1(react@19.1.1):