+
+
-
- $0
-
-
-
-
+ No Hidden Fees
- )}
-
-
- )
-}
-
-export function PricingSection() {
- const t = useTranslations("about.pricing")
- const tFeatures = useTranslations("about.pricing.features")
-
- const featureKeys = [
- "unlimitedQuestions",
- "fullQuizAccess",
- "globalLeaderboard",
- "progressTracking",
- "communityChallenges",
- "mobileFriendly"
- ] as const
-
- return (
-
-
-
-
-
-
-
- {t("title")}
- {t("titleHighlight")}
+
+ Invest in your brain,
+ not our subscriptions.
-
- {t("subtitle")}
+
+ We believe knowledge should be accessible. So we don't sell courses. But servers heat up and coffee runs out. The choice is yours.
-
-
-
-
-
- {t("badge")}
-
-
-
-
-
-
-
-
+
+
+
+
+
Junior Engineer
+
For those who want an offer, not expenses.
+
+
+ $0
+ / forever
+
+
+
+ {[
+ "Unlimited Questions",
+ "Full Quiz Access",
+ "No Credit Card Required",
+ "0% Guilt Trip",
+ ].map((item) => (
+
+
+
+
+ {item}
+
+ ))}
+
+
+
+
+ Personal Yacht
+
+
-
-
+
+ Start Learning
+
+
-
+
+
+ High Impact
+
-
- {featureKeys.map((featureKey) => (
-
-
-
-
-
{tFeatures(featureKey)}
+
+
+ Open Source Hero
+
+
+
For those who already landed an offer thanks to us.
+
+
+ $$$
+ / karma points
+
+
+
+ {[
+ "Keep Servers Alive",
+ "Buy Coffee for Mentors",
+ "Profile Badge (Big Flex)",
+ "Warm Fuzzy Feeling",
+ ].map((item) => (
+
+
+
- ))}
-
-
-
-
- {t("cta")}
-
+ {item}
+
+ ))}
+
+
+
+
+ We actually pay for Drizzle
+
+
+
+
+ Support the Project
+
+
-
-
- {t("coffee")}
-
-
+
+
+
+ *No developers were harmed in the making of this pricing table. Only caffeine levels were impacted.
+
-
- {t("noCard")}
-
-
-
-
+
+
)
-}
+}
\ No newline at end of file
diff --git a/frontend/components/about/SponsorsWall.tsx b/frontend/components/about/SponsorsWall.tsx
new file mode 100644
index 00000000..1b2b30d8
--- /dev/null
+++ b/frontend/components/about/SponsorsWall.tsx
@@ -0,0 +1,110 @@
+"use client"
+
+import { motion } from "framer-motion"
+import Image from "next/image"
+import Link from "next/link"
+import { Plus, Crown, Sparkles } from "lucide-react"
+import { cn } from "@/lib/utils"
+import type { Sponsor } from "@/lib/about/github-sponsors"
+
+interface SponsorsWallProps {
+ sponsors?: Sponsor[]
+}
+
+export function SponsorsWall({ sponsors = [] }: SponsorsWallProps) {
+
+ const displaySponsors = sponsors
+
+ return (
+
+
+
+
+ Latest Contributors
+
+
+
+
+
+ {displaySponsors.map((sponsor) => (
+
+ ))}
+
+ {displaySponsors.length === 0 && (
+
+ You?
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+ Join the club
+
+
+
+
+
+ 100% of funds go to server costs & coffee
+
+
+
+ )
+}
+
+function SponsorItem({ sponsor }: { sponsor: Sponsor }) {
+ const ringColor = sponsor.tierColor === 'gold' ? 'ring-[#1e5eff] dark:ring-[#ff2d55]' :
+ sponsor.tierColor === 'silver' ? 'ring-gray-400' : 'ring-orange-700/50'
+
+ return (
+
+
+
+
+ {sponsor.tierColor === 'gold' && (
+
+
+
+ )}
+
+
+
+ @{sponsor.login}
+ |
+
+ {sponsor.tierName}
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/components/about/StatsSection.tsx b/frontend/components/about/StatsSection.tsx
deleted file mode 100644
index b827181f..00000000
--- a/frontend/components/about/StatsSection.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-"use client"
-
-import { useState, useEffect } from "react"
-import { motion } from "framer-motion"
-import { Star, Linkedin, Users, Terminal } from "lucide-react"
-import { useTranslations } from "next-intl"
-
-const GITHUB_USERNAME = "DevLoversTeam"
-const GITHUB_REPO = "devlovers.net"
-const LINKEDIN_COUNT = "1.2k+"
-const ACTIVE_USERS = "1"
-const QUESTIONS_SOLVED = "0"
-
-function formatCount(num: number): string {
- if (num >= 1000) return (num / 1000).toFixed(1) + "k"
- return num.toString()
-}
-
-export function StatsSection() {
- const t = useTranslations("about.stats")
- const [githubStars, setGithubStars] = useState("...")
-
- useEffect(() => {
- fetch(`https://api.github.com/repos/${GITHUB_USERNAME}/${GITHUB_REPO}`)
- .then(res => res.json())
- .then(data => {
- const stars = data.stargazers_count
- ? formatCount(data.stargazers_count)
- : "2.5k"
- setGithubStars(stars)
- })
- .catch(() => setGithubStars("2.5k"))
- }, [])
-
- const stats = [
- { key: "stars", value: githubStars, label: t("githubStars"), icon: Star },
- { key: "linkedin", value: LINKEDIN_COUNT, label: t("followers"), icon: Linkedin },
- { key: "users", value: ACTIVE_USERS, label: t("activeDevs"), icon: Users },
- { key: "solved", value: QUESTIONS_SOLVED, label: t("solved"), icon: Terminal },
- ]
-
- return (
-
-
- {stats.map((item, index) => {
- const Icon = item.icon
- return (
-
-
-
-
-
-
- {item.value}
-
-
-
- {item.label}
-
-
- )
- })}
-
-
- )
-}
diff --git a/frontend/components/about/TopicsSection.tsx b/frontend/components/about/TopicsSection.tsx
new file mode 100644
index 00000000..e3e82bd3
--- /dev/null
+++ b/frontend/components/about/TopicsSection.tsx
@@ -0,0 +1,98 @@
+"use client"
+
+import { motion } from "framer-motion"
+import { ArrowUpRight } from "lucide-react"
+import { TOPICS, type Topic } from "@/data/about"
+import Image from "next/image"
+import Link from "next/link"
+
+export function TopicsSection() {
+ return (
+
+
+
+
+
+
+ / The Ecosystem
+
+
+ Master your
+
+ entire stack
+
+
+
+
+
+ From frontend frameworks to backend logic. We cover the key technologies for 2026.
+
+
+
+
+ {TOPICS.map((topic, i) => (
+
+ ))}
+
+
+
+
+ )
+}
+
+function TopicCard({ topic, index }: { topic: Topic, index: number }) {
+ return (
+
+
+
+
+
+
+
+
+ {topic.name}
+
+
+ {topic.questions}
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/components/ui/github-star-button.tsx b/frontend/components/ui/github-star-button.tsx
new file mode 100644
index 00000000..e69de29b
diff --git a/frontend/data/about.ts b/frontend/data/about.ts
new file mode 100644
index 00000000..e8ad28a5
--- /dev/null
+++ b/frontend/data/about.ts
@@ -0,0 +1,146 @@
+import { Twitter, Linkedin, Github } from "lucide-react"
+
+export const TESTIMONIALS = [
+ {
+ name: "Alex Chen",
+ role: "Frontend @ Meta",
+ avatar: "AC",
+ content: "Cheap therapy for React devs.",
+ platform: "Twitter",
+ icon: Twitter,
+ color: "text-sky-500 bg-sky-500/10"
+ },
+ {
+ name: "Sarah J.",
+ role: "Senior SWE @ Google",
+ avatar: "SJ",
+ content: "Harder than my actual interview. 10/10.",
+ platform: "LinkedIn",
+ icon: Linkedin,
+ color: "text-blue-600 bg-blue-600/10"
+ },
+ {
+ name: "git_push_force",
+ role: "Open Source Contributor",
+ avatar: "GP",
+ content: "Found a bug in the quiz, reported it, got points. Now I'm addicted to fixing your typos.",
+ platform: "GitHub",
+ icon: Github,
+ color: "text-gray-900 dark:text-white bg-gray-500/10"
+ },
+ {
+ name: "Emily Park",
+ role: "Full Stack @ Vercel",
+ avatar: "EP",
+ content: "This is the only place where 'centering a div' is explained like I'm 5. Bless you.",
+ platform: "Twitter",
+ icon: Twitter,
+ color: "text-sky-500 bg-sky-500/10"
+ },
+ {
+ name: "David Kim",
+ role: "Staff Engineer @ Netflix",
+ avatar: "DK",
+ content: "I use the Q&A section to win arguments with my juniors. Don't tell them.",
+ platform: "LinkedIn",
+ icon: Linkedin,
+ color: "text-blue-600 bg-blue-600/10"
+ },
+]
+
+export const TOPICS = [
+ {
+ id: "git",
+ name: "Git & Version Control",
+ questions: "90+ Questions",
+ icon: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/git/git-original.svg",
+ color: "group-hover:border-[#F05032]/50 group-hover:bg-[#F05032]/10",
+ glow: "bg-[#F05032]",
+ href: "/q&a"
+ },
+ {
+ id: "html",
+ name: "HTML5 & Semantic",
+ questions: "120+ Questions",
+ icon: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/html5/html5-original.svg",
+ color: "group-hover:border-[#E34F26]/50 group-hover:bg-[#E34F26]/10",
+ glow: "bg-[#E34F26]",
+ href: "/q&a/?category=html"
+ },
+ {
+ id: "css",
+ name: "CSS3 & Responsive",
+ questions: "180+ Questions",
+ icon: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/css3/css3-original.svg",
+ color: "group-hover:border-[#1572B6]/50 group-hover:bg-[#1572B6]/10",
+ glow: "bg-[#1572B6]",
+ href: "/q&a/?category=css"
+ },
+ {
+ id: "js",
+ name: "JavaScript (ES6+)",
+ questions: "450+ Questions",
+ icon: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/javascript/javascript-original.svg",
+ color: "group-hover:border-[#F7DF1E]/50 group-hover:bg-[#F7DF1E]/10",
+ glow: "bg-[#F7DF1E]",
+ href: "/q&a/?category=javascript"
+ },
+ {
+ id: "ts",
+ name: "TypeScript",
+ questions: "210+ Questions",
+ icon: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/typescript/typescript-original.svg",
+ color: "group-hover:border-[#3178C6]/50 group-hover:bg-[#3178C6]/10",
+ glow: "bg-[#3178C6]",
+ href: "/q&a/?category=typescript"
+ },
+ {
+ id: "react",
+ name: "React.js Ecosystem",
+ questions: "320+ Questions",
+ icon: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/react/react-original.svg",
+ color: "group-hover:border-[#61DAFB]/50 group-hover:bg-[#61DAFB]/10",
+ glow: "bg-[#61DAFB]",
+ href: "/q&a/?category=react"
+ },
+ {
+ id: "next",
+ name: "Next.js & SSR",
+ questions: "140+ Questions",
+ icon: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/nextjs/nextjs-original.svg",
+ color: "group-hover:border-black/50 dark:group-hover:border-white/50 group-hover:bg-black/5 dark:group-hover:bg-white/10",
+ glow: "bg-black dark:bg-white",
+ className: "dark:invert",
+ href: "/q&a/?category=next"
+ },
+ {
+ id: "vue",
+ name: "Vue.js",
+ questions: "110+ Questions",
+ icon: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/vuejs/vuejs-original.svg",
+ color: "group-hover:border-[#4FC08D]/50 group-hover:bg-[#4FC08D]/10",
+ glow: "bg-[#4FC08D]",
+ href: "/q&a/?category=vue"
+ },
+ {
+ id: "angular",
+ name: "Angular",
+ questions: "95+ Questions",
+ icon: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/angularjs/angularjs-original.svg",
+ color: "group-hover:border-[#DD0031]/50 group-hover:bg-[#DD0031]/10",
+ glow: "bg-[#DD0031]",
+ href: "/q&a/?category=angular"
+ },
+ {
+ id: "node",
+ name: "Node.js & Backend",
+ questions: "150+ Questions",
+ icon: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/nodejs/nodejs-original.svg",
+ color: "group-hover:border-[#339933]/50 group-hover:bg-[#339933]/10",
+ glow: "bg-[#339933]",
+ href: "/q&a/?category=node"
+ },
+]
+
+export type Topic = typeof TOPICS[number];
+export type Testimonial = typeof TESTIMONIALS[number];
\ No newline at end of file
diff --git a/frontend/lib/about/github-sponsors.ts b/frontend/lib/about/github-sponsors.ts
new file mode 100644
index 00000000..be263e4c
--- /dev/null
+++ b/frontend/lib/about/github-sponsors.ts
@@ -0,0 +1,88 @@
+import "server-only"
+
+export interface Sponsor {
+ login: string
+ name: string
+ avatarUrl: string
+ tierName: string
+ tierColor: "gold" | "silver" | "bronze"
+ monthlyPrice: number
+}
+
+function getTierDetails(amount: number): { name: string; color: "gold" | "silver" | "bronze" } {
+ if (amount >= 100) return { name: "🏆 Core Supporter", color: "gold" }
+ if (amount >= 50) return { name: "🎓 Impact Support", color: "silver" }
+ if (amount >= 25) return { name: "🧠 Community Support", color: "silver" }
+ if (amount >= 10) return { name: "🚀 Early Support", color: "bronze" }
+ return { name: "☕ Coffee Support", color: "bronze" }
+}
+
+export async function getSponsors(): Promise
{
+ const token = process.env.GITHUB_SPONSORS_TOKEN
+
+ if (!token) {
+ console.warn("⚠️ GITHUB_SPONSORS_TOKEN is missing.")
+ return []
+ }
+
+ const query = `
+ query {
+ organization(login: "DevLoversTeam") {
+ sponsorshipsAsMaintainer(first: 100, orderBy: {field: CREATED_AT, direction: DESC}, includePrivate: false) {
+ nodes {
+ tier { monthlyPriceInDollars }
+ sponsorEntity {
+ ... on User { login name avatarUrl }
+ ... on Organization { login name avatarUrl }
+ }
+ }
+ }
+ }
+ }
+ `
+
+ try {
+ const res = await fetch("https://api.github.com/graphql", {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${token}`,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({ query }),
+ cache: "no-store" // Завжди свіжі дані
+ })
+
+ const json = await res.json()
+
+ if (json.errors) {
+ console.error("❌ GitHub API Error:", json.errors[0].message)
+ return []
+ }
+
+ const rawNodes = json.data?.organization?.sponsorshipsAsMaintainer?.nodes || []
+
+ console.log(`✅ GitHub: Found ${rawNodes.length} sponsors for Organization`)
+
+ const sponsors: Sponsor[] = rawNodes.map((node: any) => {
+ const price = node.tier?.monthlyPriceInDollars || 0
+ const { name, color } = getTierDetails(price)
+
+ if (!node.sponsorEntity) return null
+
+ return {
+ login: node.sponsorEntity.login,
+ name: node.sponsorEntity.name || node.sponsorEntity.login,
+ avatarUrl: node.sponsorEntity.avatarUrl,
+ monthlyPrice: price,
+ tierName: name,
+ tierColor: color,
+ }
+ }).filter(Boolean) as Sponsor[]
+
+ return sponsors
+
+ } catch (error) {
+ console.error("❌ Failed to fetch sponsors:", error)
+ return []
+ }
+}
\ No newline at end of file
diff --git a/frontend/lib/about/stats.ts b/frontend/lib/about/stats.ts
new file mode 100644
index 00000000..683ab6d4
--- /dev/null
+++ b/frontend/lib/about/stats.ts
@@ -0,0 +1,64 @@
+import { db } from '@/db'
+import { users } from '@/db/schema/users'
+import { quizAttempts } from '@/db/schema/quiz'
+import { count } from 'drizzle-orm'
+import { unstable_cache } from 'next/cache'
+
+export interface PlatformStats {
+ githubStars: string
+ linkedinFollowers: string
+ activeUsers: string
+ questionsSolved: string
+}
+
+const formatMetric = (n: number) => {
+ if (n >= 1000) return (n / 1000).toFixed(1) + 'k+'
+ return n.toString()
+}
+
+export const getPlatformStats = unstable_cache(
+ async (): Promise => {
+ let stars = 125
+ try {
+ const headers: HeadersInit = {}
+ if (process.env.GITHUB_TOKEN) headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`
+
+ const res = await fetch('https://api.github.com/repos/DevLoversTeam/devlovers.net', {
+ headers,
+ cache: 'no-store'
+ })
+
+ if (res.ok) {
+ const data = await res.json()
+ stars = data.stargazers_count
+ }
+ } catch (e) {
+ console.error("GitHub Fetch Error:", e)
+ }
+
+ const linkedinCount = process.env.LINKEDIN_FOLLOWER_COUNT ? parseInt(process.env.LINKEDIN_FOLLOWER_COUNT) : 1342
+
+ let totalUsers = 243
+ let solvedTests = 1890
+ try {
+ const [[u], [q]] = await Promise.all([
+ db.select({ value: count() }).from(users),
+ db.select({ value: count() }).from(quizAttempts)
+ ])
+
+ if (u) totalUsers = u.value
+ if (q) solvedTests = q.value
+ } catch (e) {
+ console.error("DB Fetch Error:", e)
+ }
+
+ return {
+ githubStars: formatMetric(stars),
+ linkedinFollowers: formatMetric(linkedinCount),
+ activeUsers: formatMetric(totalUsers),
+ questionsSolved: formatMetric(solvedTests)
+ }
+ },
+ ['platform-stats'],
+ { revalidate: 3600 }
+)
\ No newline at end of file
diff --git a/frontend/next.config.ts b/frontend/next.config.ts
index 37bb7f58..e021c2e1 100644
--- a/frontend/next.config.ts
+++ b/frontend/next.config.ts
@@ -36,8 +36,18 @@ const nextConfig: NextConfig = {
hostname: "api.dicebear.com",
pathname: "/**",
},
+ {
+ protocol: "https",
+ hostname: "github.com",
+ pathname: "/**",
+ },
+ {
+ protocol: 'https',
+ hostname: 'cdn.jsdelivr.net',
+ pathname: '/**',
+ },
],
},
};
-export default withNextIntl(nextConfig);
+export default withNextIntl(nextConfig);
\ No newline at end of file
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index a4f372be..3c41f9d1 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -48,9 +48,9 @@
"@testing-library/react": "^16.3.1",
"@testing-library/user-event": "^14.6.1",
"@types/jsonwebtoken": "^9.0.10",
- "@types/node": "^20",
+ "@types/node": "^20.19.30",
"@types/nodemailer": "^7.0.4",
- "@types/react": "^19",
+ "@types/react": "^19.2.8",
"@types/react-dom": "^19",
"drizzle-kit": "^0.18.1",
"eslint": "^9",
@@ -915,7 +915,6 @@
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.28.6",
"@babel/generator": "^7.28.6",
@@ -1223,7 +1222,6 @@
}
],
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=18"
},
@@ -1267,7 +1265,6 @@
}
],
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -1318,6 +1315,7 @@
"os": [
"aix"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1335,6 +1333,7 @@
"os": [
"android"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1352,6 +1351,7 @@
"os": [
"android"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1369,6 +1369,7 @@
"os": [
"android"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1386,6 +1387,7 @@
"os": [
"darwin"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1403,6 +1405,7 @@
"os": [
"darwin"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1420,6 +1423,7 @@
"os": [
"freebsd"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1437,6 +1441,7 @@
"os": [
"freebsd"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1454,6 +1459,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1471,6 +1477,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1488,6 +1495,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1505,6 +1513,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1522,6 +1531,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1539,6 +1549,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1556,6 +1567,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1573,6 +1585,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1590,6 +1603,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1607,6 +1621,7 @@
"os": [
"netbsd"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1624,6 +1639,7 @@
"os": [
"netbsd"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1641,6 +1657,7 @@
"os": [
"openbsd"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1658,6 +1675,7 @@
"os": [
"openbsd"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1675,6 +1693,7 @@
"os": [
"openharmony"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1692,6 +1711,7 @@
"os": [
"sunos"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1709,6 +1729,7 @@
"os": [
"win32"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1726,6 +1747,7 @@
"os": [
"win32"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1743,6 +1765,7 @@
"os": [
"win32"
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -4577,7 +4600,6 @@
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-8.6.1.tgz",
"integrity": "sha512-UJ05U2062XDgydbUcETH1AoRQLNhigQ2KmDn1BG8sC3xfzu6JKg95Qt6YozdzFpxl1Npii/02m2LEWFt1RYjVA==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12.16"
}
@@ -5043,7 +5065,6 @@
"integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
@@ -5239,7 +5260,6 @@
"integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==",
"devOptional": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -5250,7 +5270,6 @@
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"devOptional": true,
"license": "MIT",
- "peer": true,
"peerDependencies": {
"@types/react": "^19.2.0"
}
@@ -5300,7 +5319,6 @@
"integrity": "sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.53.0",
"@typescript-eslint/types": "8.53.0",
@@ -5949,7 +5967,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -6363,7 +6380,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -7914,7 +7930,6 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -8100,7 +8115,6 @@
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.9",
@@ -9698,7 +9712,6 @@
"integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@acemir/cssom": "^0.9.28",
"@asamuzakjp/dom-selector": "^6.7.6",
@@ -10504,7 +10517,6 @@
"resolved": "https://registry.npmjs.org/next/-/next-16.1.3.tgz",
"integrity": "sha512-gthG3TRD+E3/mA0uDQb9lqBmx1zVosq5kIwxNN6+MRNd085GzD+9VXMPUs+GGZCbZ+GDZdODUq4Pm7CTXK6ipw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@next/env": "16.1.3",
"@swc/helpers": "0.5.15",
@@ -10628,6 +10640,16 @@
}
}
},
+ "node_modules/next-intl/node_modules/@swc/helpers": {
+ "version": "0.5.18",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.18.tgz",
+ "integrity": "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
"node_modules/next-themes": {
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz",
@@ -10983,7 +11005,6 @@
"resolved": "https://registry.npmjs.org/pg/-/pg-8.17.1.tgz",
"integrity": "sha512-EIR+jXdYNSMOrpRp7g6WgQr7SaZNZfS7IzZIO0oTNEeibq956JxeD15t3Jk3zZH0KH8DmOIx38qJfQenoE8bXQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"pg-connection-string": "^2.10.0",
"pg-pool": "^3.11.0",
@@ -11147,7 +11168,6 @@
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.8.tgz",
"integrity": "sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==",
"license": "Unlicense",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -11314,7 +11334,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -11324,7 +11343,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -12309,7 +12327,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -13167,7 +13184,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -13352,7 +13368,6 @@
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@@ -13930,7 +13945,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -14309,7 +14323,6 @@
"integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==",
"dev": true,
"license": "MIT",
- "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
diff --git a/frontend/package.json b/frontend/package.json
index ea453a88..0f0a0651 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -59,9 +59,9 @@
"@testing-library/react": "^16.3.1",
"@testing-library/user-event": "^14.6.1",
"@types/jsonwebtoken": "^9.0.10",
- "@types/node": "^20",
+ "@types/node": "^20.19.30",
"@types/nodemailer": "^7.0.4",
- "@types/react": "^19",
+ "@types/react": "^19.2.8",
"@types/react-dom": "^19",
"drizzle-kit": "^0.18.1",
"eslint": "^9",