diff --git a/app/[hackathon]/InteractionClient.tsx b/app/[hackathon]/InteractionClient.tsx index ef9958b..1ee7a7a 100644 --- a/app/[hackathon]/InteractionClient.tsx +++ b/app/[hackathon]/InteractionClient.tsx @@ -489,10 +489,10 @@ export default function InteractionClient() { switch (status) { case 'upcoming': return Upcoming - case 'active': - return Active - {daysRemaining} days left - case 'ended': - return Submission Ended + case 'accepting-submissions': + return Accepting Submissions - {daysRemaining} days left + case 'judging-submissions': + return Judging Submissions case 'concluded': return Concluded default: @@ -604,7 +604,7 @@ export default function InteractionClient() {
-
+

Submission Deadline

@@ -745,7 +745,7 @@ export default function InteractionClient() { )} {/* Project Submission */} - {status === 'active' && ( + {status === 'accepting-submissions' && (

diff --git a/app/createHackathon/page.tsx b/app/createHackathon/page.tsx index 9307421..936698b 100644 --- a/app/createHackathon/page.tsx +++ b/app/createHackathon/page.tsx @@ -48,6 +48,7 @@ export default function CreateHackathon() { const [timezoneMode, setTimezoneMode] = useState('local') const [prizePoolType, setPrizePoolType] = useState<'eth' | 'token'>('eth') + const [tokenSymbol, setTokenSymbol] = useState('') const [judges, setJudges] = useState([ { address: '', name: '', tokens: '' } @@ -88,6 +89,33 @@ export default function CreateHackathon() { } }) + // Fetch token symbol + const { data: fetchedTokenSymbol } = useReadContract({ + address: formData.tokenAddress as `0x${string}`, + abi: [ + { + "inputs": [], + "name": "symbol", + "outputs": [{"internalType": "string", "name": "", "type": "string"}], + "stateMutability": "view", + "type": "function" + } + ] as const, + functionName: 'symbol', + query: { + enabled: prizePoolType === 'token' && isAddress(formData.tokenAddress) + } + }) + + // Update token symbol when fetched + useEffect(() => { + if (fetchedTokenSymbol) { + setTokenSymbol(String(fetchedTokenSymbol)) + } else { + setTokenSymbol('') + } + }, [fetchedTokenSymbol]) + // Smart contract has been fixed! // Factory now transfers tokens directly from user to the created Hackathon contract @@ -250,6 +278,7 @@ export default function CreateHackathon() { } // Check judges + const judgeAddresses = new Set() for (let i = 0; i < judges.length; i++) { const judge = judges[i] if (!judge.address || !judge.name || !judge.tokens) { @@ -262,6 +291,14 @@ export default function CreateHackathon() { return false } + // Check for duplicate addresses + const normalizedAddress = judge.address.toLowerCase() + if (judgeAddresses.has(normalizedAddress)) { + setValidationError(`Duplicate judge address found. Each judge must have a unique wallet address.`) + return false + } + judgeAddresses.add(normalizedAddress) + const tokens = parseInt(judge.tokens) if (isNaN(tokens) || tokens <= 0) { setValidationError(`Invalid token amount for judge ${i + 1}`) @@ -399,34 +436,31 @@ export default function CreateHackathon() {
{/* Basic Information */} - - - - Basic Information - - - + {/* Hackathon Name */}
-
- - +
+
+ + + +
+ setFormData({ ...formData, name: e.target.value })} + placeholder="Enter hackathon name" + className="border-amber-200 focus:border-amber-500 bg-white text-gray-900 placeholder:text-gray-500 flex-1" + />
- setFormData({ ...formData, name: e.target.value })} - placeholder="Enter hackathon name" - className="border-amber-200 focus:border-amber-500 bg-white text-gray-900 placeholder:text-gray-500" - /> {showInfo.name && ( -

+

Choose a unique and descriptive name for your hackathon

)} @@ -437,8 +471,8 @@ export default function CreateHackathon() { {/* Date and Time */}
{/* Timezone Toggle */} -
-
+
+
{showInfo.timezone && ( -

+

Choose how you want to input times. Local time will be converted to UTC for storage on the blockchain. All times are ultimately stored and displayed in UTC format.

@@ -487,68 +521,64 @@ export default function CreateHackathon() {
- + +
+
+ setFormData({ ...formData, startDate: e.target.value })} + className="border-blue-200 focus:border-blue-500 bg-white text-gray-900 [&::-webkit-calendar-picker-indicator]:opacity-100 [&::-webkit-calendar-picker-indicator]:cursor-pointer" + style={{ + colorScheme: 'light' + }} + /> + setFormData({ ...formData, startTime: e.target.value })} + className="border-blue-200 focus:border-blue-500 bg-white text-gray-900 [&::-webkit-calendar-picker-indicator]:opacity-100 [&::-webkit-calendar-picker-indicator]:cursor-pointer" + style={{ + colorScheme: 'light' + }} + />
-
- setFormData({ ...formData, startDate: e.target.value })} - className="border-blue-200 focus:border-blue-500 bg-white text-gray-900" - style={{ - colorScheme: 'light' - }} - /> - setFormData({ ...formData, startTime: e.target.value })} - className="border-blue-200 focus:border-blue-500 bg-white text-gray-900" - style={{ - colorScheme: 'light' - }} - />
-
-
-
- - -
-
- setFormData({ ...formData, endDate: e.target.value })} - className="border-red-200 focus:border-red-500 bg-white text-gray-900" - style={{ - colorScheme: 'light' - }} - /> - setFormData({ ...formData, endTime: e.target.value })} - className="border-red-200 focus:border-red-500 bg-white text-gray-900" - style={{ - colorScheme: 'light' - }} - /> +
+
+ + +
+
+ setFormData({ ...formData, endDate: e.target.value })} + className="border-red-200 focus:border-red-500 bg-white text-gray-900 [&::-webkit-calendar-picker-indicator]:opacity-100 [&::-webkit-calendar-picker-indicator]:cursor-pointer" + style={{ + colorScheme: 'light' + }} + /> + setFormData({ ...formData, endTime: e.target.value })} + className="border-red-200 focus:border-red-500 bg-white text-gray-900 [&::-webkit-calendar-picker-indicator]:opacity-100 [&::-webkit-calendar-picker-indicator]:cursor-pointer" + style={{ + colorScheme: 'light' + }} + /> +
-
{/* Prize Pool */}
{/* Prize Pool Type Toggle */} -
-
+
+
{showInfo.prizePoolType && ( -

- Choose whether to fund the prize pool with native ETH (sent with transaction) or with an ERC-20 token (requires approval). - Token-based hackathons are now fully supported! +

+ Choose whether to fund the prize pool with native ETH or with an ERC20 token.

)} + {/* Token Fields - Parallel Layout */} + {prizePoolType === 'token' && ( +
+ {/* Token Address */} +
+
+
+ + +
+
+ setFormData({ ...formData, tokenAddress: e.target.value })} + placeholder="0x..." + className="font-mono text-sm border-yellow-200 focus:border-yellow-500 bg-white text-gray-900 placeholder:text-gray-500" + /> +
+ {/* Prize Pool Amount */} +
+
+
+ + + +
+
+ setFormData({ ...formData, prizePool: e.target.value })} + placeholder={`Enter amount in ${tokenSymbol || 'tokens'}`} + className="border-yellow-200 focus:border-yellow-500 bg-white text-gray-900 placeholder:text-gray-500" + /> +
+
+ )} - {/* Token Address Input (only show when token is selected) */} - {prizePoolType === 'token' && ( + {/* ETH Prize Pool Amount (when ETH is selected) */} + {prizePoolType === 'eth' && (
- - setFormData({ ...formData, tokenAddress: e.target.value })} - placeholder="0x..." - className="font-mono text-sm border-yellow-200 focus:border-yellow-500 bg-white text-gray-900 placeholder:text-gray-500" - /> +
+
+ + + +
+ setFormData({ ...formData, prizePool: e.target.value })} + placeholder="Enter prize pool amount in ETH" + className="border-yellow-200 focus:border-yellow-500 bg-white text-gray-900 placeholder:text-gray-500 flex-1" + /> +
)} - {/* Prize Pool Amount */} -
- - setFormData({ ...formData, prizePool: e.target.value })} - placeholder={`Enter prize pool amount in ${prizePoolType === 'eth' ? 'ETH' : 'tokens'}`} - className="border-yellow-200 focus:border-yellow-500 bg-white text-gray-900 placeholder:text-gray-500" - /> - {showInfo.prizePool && ( -

- The total prize pool that will be distributed to winners based on voting results -

- )} -
+ {showInfo.prizePool && ( +

+ The total prize pool that will be distributed to winners based on voting results +

+ )}
@@ -683,7 +762,10 @@ export default function CreateHackathon() {
- +
+ + +
updateJudge(index, 'address', e.target.value)} @@ -692,7 +774,10 @@ export default function CreateHackathon() { />
- +
+ + +
updateJudge(index, 'name', e.target.value)} @@ -701,7 +786,10 @@ export default function CreateHackathon() { />
- +
+ + +
{ switch (status) { - case 'active': return 'bg-green-100 text-green-800 border-green-200' + case 'accepting-submissions': return 'bg-green-100 text-green-800 border-green-200' case 'upcoming': return 'bg-blue-100 text-blue-800 border-blue-200' - case 'ended': return 'bg-orange-100 text-orange-800 border-orange-200' + case 'judging-submissions': return 'bg-orange-100 text-orange-800 border-orange-200' case 'concluded': return 'bg-gray-100 text-gray-800 border-gray-200' default: return 'bg-gray-100 text-gray-800 border-gray-200' } @@ -240,18 +240,18 @@ export default function ExplorerPage() { let matchesStatus = true if (statusFilter !== "All Status") { const status = getHackathonStatus(hackathon.startTime, hackathon.endTime, hackathon.concluded) - matchesStatus = statusFilter.toLowerCase() === status + matchesStatus = statusFilter === status } return matchesSearch && matchesStatus }) - // Sort hackathons by status priority (active first, then upcoming, then ended/concluded) + // Sort hackathons by status priority (accepting-submissions first, then upcoming, then judging-submissions/concluded) const sortedHackathons = filteredHackathons.sort((a, b) => { const statusA = getHackathonStatus(a.startTime, a.endTime, a.concluded) const statusB = getHackathonStatus(b.startTime, b.endTime, b.concluded) - const statusPriority = { 'active': 0, 'upcoming': 1, 'ended': 2, 'concluded': 3 } + const statusPriority = { 'accepting-submissions': 0, 'upcoming': 1, 'judging-submissions': 2, 'concluded': 3 } return statusPriority[statusA] - statusPriority[statusB] }) @@ -356,9 +356,9 @@ export default function ExplorerPage() { All Status - Active + Accepting Submissions Upcoming - Ended + Judging Submissions Concluded @@ -367,15 +367,15 @@ export default function ExplorerPage() { {/* Hackathons List */}
- {/* Ongoing Hackathons */} - {sortedHackathons.filter(h => getHackathonStatus(h.startTime, h.endTime, h.concluded) === 'active').length > 0 && ( + {/* Active Hackathons */} + {sortedHackathons.filter(h => getHackathonStatus(h.startTime, h.endTime, h.concluded) === 'accepting-submissions').length > 0 && (

- đŸ”Ĩ Ongoing Hackathons + đŸ”Ĩ Active Hackathons

-
+
{sortedHackathons - .filter(h => getHackathonStatus(h.startTime, h.endTime, h.concluded) === 'active') + .filter(h => getHackathonStatus(h.startTime, h.endTime, h.concluded) === 'accepting-submissions') .map((hackathon) => { const status = getHackathonStatus(hackathon.startTime, hackathon.endTime, hackathon.concluded) return ( @@ -384,56 +384,65 @@ export default function ExplorerPage() { href={`/h?hackAddr=${hackathon.contractAddress}&chainId=${chainId}`} className="block" > -
+
{/* Gradient background overlay based on status */}
-
- {/* Left section - Name and Status */} -
-
-

- {hackathon.hackathonName} -

- - đŸ”Ĩ {status.toUpperCase()} - -
-
- - {/* Center section - Block Image */} -
-
+
+ {/* Top section - Image */} +
+
Blockchain Block
- {/* Right section - Date and Prize */} -
-
- - - {formatDate(hackathon.startTime)} - {formatDate(hackathon.endTime)} - -
-
+ {/* Title */} +

+ {hackathon.hackathonName} +

+ + {/* Status Badge */} +
+ + đŸ”Ĩ {status === 'accepting-submissions' ? 'ACCEPTING SUBMISSIONS' : + status === 'judging-submissions' ? 'JUDGING SUBMISSIONS' : + status === 'upcoming' ? 'UPCOMING' : 'CONCLUDED'} + +
+ + {/* Date */} +
+ + + {formatDate(hackathon.startTime)} + +
+
+ + to {formatDate(hackathon.endTime)} + +
+ + {/* Prize - Push to bottom */} +
+
- {formatPrizeAmount(hackathon)} + {formatPrizeAmount(hackathon)}
@@ -446,14 +455,14 @@ export default function ExplorerPage() { )} {/* Other Hackathons */} - {sortedHackathons.filter(h => getHackathonStatus(h.startTime, h.endTime, h.concluded) !== 'active').length > 0 && ( + {sortedHackathons.filter(h => getHackathonStatus(h.startTime, h.endTime, h.concluded) !== 'accepting-submissions').length > 0 && (

Upcoming & Past Hackathons

-
+
{sortedHackathons - .filter(h => getHackathonStatus(h.startTime, h.endTime, h.concluded) !== 'active') + .filter(h => getHackathonStatus(h.startTime, h.endTime, h.concluded) !== 'accepting-submissions') .map((hackathon) => { const status = getHackathonStatus(hackathon.startTime, hackathon.endTime, hackathon.concluded) return ( @@ -462,57 +471,66 @@ export default function ExplorerPage() { href={`/h?hackAddr=${hackathon.contractAddress}&chainId=${chainId}`} className="block" > -
+
{/* Gradient background overlay based on status */}
-
- {/* Left section - Name and Status */} -
-
-

- {hackathon.hackathonName} -

- - {status === 'upcoming' && '⏰'} - {status === 'ended' && 'âšī¸'} - {status === 'concluded' && '✅'} - {status.toUpperCase()} - -
-
- - {/* Center section - Block Image */} -
-
+
+ {/* Top section - Image */} +
+
Blockchain Block
- {/* Right section - Date and Prize */} -
-
- - - {formatDate(hackathon.startTime)} - {formatDate(hackathon.endTime)} - -
-
+ {/* Title */} +

+ {hackathon.hackathonName} +

+ + {/* Status Badge */} +
+ + {status === 'upcoming' && '⏰'} + {status === 'judging-submissions' && 'âš–ī¸'} + {status === 'concluded' && '✅'} + {status === 'accepting-submissions' ? 'ACCEPTING SUBMISSIONS' : + status === 'judging-submissions' ? 'JUDGING SUBMISSIONS' : + status === 'upcoming' ? 'UPCOMING' : 'CONCLUDED'} + +
+ + {/* Date */} +
+ + + {formatDate(hackathon.startTime)} + +
+
+ + to {formatDate(hackathon.endTime)} + +
+ + {/* Prize - Push to bottom */} +
+
- {formatPrizeAmount(hackathon)} + {formatPrizeAmount(hackathon)}
diff --git a/app/myHackathons/page.tsx b/app/myHackathons/page.tsx index 31a55e1..502604a 100644 --- a/app/myHackathons/page.tsx +++ b/app/myHackathons/page.tsx @@ -514,9 +514,9 @@ export default function MyHackathonsPage() {
- {status === 'active' ? `${getDaysRemaining(hackathon.endTime)} days left` : + {status === 'accepting-submissions' ? `${getDaysRemaining(hackathon.endTime)} days left` : status === 'upcoming' ? 'Not started' : - status === 'ended' ? 'Ended' : 'Concluded'} + status === 'judging-submissions' ? 'Judging Submissions' : 'Concluded'}
@@ -610,14 +610,14 @@ export default function MyHackathonsPage() {
- {status === 'active' ? `${getDaysRemaining(hackathon.endTime)} days left to submit` : + {status === 'accepting-submissions' ? `${getDaysRemaining(hackathon.endTime)} days left to submit` : status === 'upcoming' ? 'Not started' : - status === 'ended' ? 'Voting period - submissions closed' : 'Concluded'} + status === 'judging-submissions' ? 'Voting period - submissions closed' : 'Concluded'}
- {status === 'ended' && userJudge.tokensRemaining > 0 && ( + {status === 'judging-submissions' && userJudge.tokensRemaining > 0 && ( + + +
)}
diff --git a/app/organizer/[address]/OrganizerClient.tsx b/app/organizer/[address]/OrganizerClient.tsx index 0745cb3..39f1841 100644 --- a/app/organizer/[address]/OrganizerClient.tsx +++ b/app/organizer/[address]/OrganizerClient.tsx @@ -442,11 +442,11 @@ export default function OrganizerClient({ address }: OrganizerClientProps) {
{/* Gradient background overlay based on status */}
@@ -522,7 +522,7 @@ export default function OrganizerClient({ address }: OrganizerClientProps) {
@@ -536,7 +536,7 @@ export default function OrganizerClient({ address }: OrganizerClientProps) { {status === 'upcoming' && '⏰'} - {status === 'ended' && 'âšī¸'} + {status === 'judging-submissions' && 'âšī¸'} {status === 'concluded' && '✅'} {status.toUpperCase()} diff --git a/components/hackathon-card.tsx b/components/hackathon-card.tsx index 34221b8..70b7efb 100644 --- a/components/hackathon-card.tsx +++ b/components/hackathon-card.tsx @@ -23,10 +23,10 @@ export default function HackathonCard({ hackathon, showJoinButton = true }: Hack switch (status) { case 'upcoming': return Upcoming - case 'active': - return {daysRemaining} days left - case 'ended': - return Ended + case 'accepting-submissions': + return Accepting Submissions + case 'judging-submissions': + return Judging Submissions case 'concluded': return Concluded default: diff --git a/hooks/useHackathons.ts b/hooks/useHackathons.ts index addb1ba..dfd8120 100644 --- a/hooks/useHackathons.ts +++ b/hooks/useHackathons.ts @@ -55,10 +55,10 @@ export const getDaysRemaining = (endTime: number): number => { return Math.floor(getTimeRemaining(endTime) / 86400) } -export const getHackathonStatus = (startTime: number, endTime: number, concluded: boolean): 'upcoming' | 'active' | 'ended' | 'concluded' => { +export const getHackathonStatus = (startTime: number, endTime: number, concluded: boolean): 'upcoming' | 'accepting-submissions' | 'judging-submissions' | 'concluded' => { if (concluded) return 'concluded' const now = getCurrentUTCTimestamp() if (now < startTime) return 'upcoming' - if (now > endTime) return 'ended' - return 'active' + if (now > endTime) return 'judging-submissions' + return 'accepting-submissions' } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 66687d3..914deb1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "lucide-react": "^0.454.0", "next": "15.2.4", "next-themes": "^0.4.4", + "pino-pretty": "^13.1.1", "react": "^18.3.1", "react-day-picker": "8.10.1", "react-dom": "^18.3.1", @@ -6008,6 +6009,12 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "license": "MIT" + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -6236,6 +6243,15 @@ "url": "https://github.com/sponsors/kossnocorp" } }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", @@ -6751,6 +6767,12 @@ "node": ">=12.0.0" } }, + "node_modules/fast-copy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", + "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7059,6 +7081,12 @@ "node": ">= 0.4" } }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, "node_modules/idb-keyval": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", @@ -7310,6 +7338,15 @@ "jiti": "bin/jiti.js" } }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -7495,6 +7532,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -7961,6 +8007,57 @@ "split2": "^4.0.0" } }, + "node_modules/pino-pretty": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.1.tgz", + "integrity": "sha512-TNNEOg0eA0u+/WuqH0MH0Xui7uqVk9D74ESOpjtebSQYbNWJk/dIxCXIxFsNfeN53JmtWqYHP2OrIZjT/CBEnA==", + "license": "MIT", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^3.0.2", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pump": "^3.0.0", + "secure-json-parse": "^4.0.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^5.0.2" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-pretty/node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/pino-pretty/node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty/node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/pino-std-serializers": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", @@ -8566,6 +8663,22 @@ "loose-envify": "^1.1.0" } }, + "node_modules/secure-json-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.0.0.tgz", + "integrity": "sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/semver": { "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", @@ -8911,6 +9024,18 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", diff --git a/package.json b/package.json index df24732..11ff178 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "lucide-react": "^0.454.0", "next": "15.2.4", "next-themes": "^0.4.4", + "pino-pretty": "^13.1.1", "react": "^18.3.1", "react-day-picker": "8.10.1", "react-dom": "^18.3.1",