feat: implement internationalization with next-intl and add core portfolio sections
This commit is contained in:
143
messages/en.json
Normal file
143
messages/en.json
Normal file
@@ -0,0 +1,143 @@
|
||||
{
|
||||
"Navigation": {
|
||||
"experience": "Experience",
|
||||
"techStack": "Tech Stack",
|
||||
"projects": "Projects",
|
||||
"contact": "Contact"
|
||||
},
|
||||
"Hero": {
|
||||
"badge": "Available for opportunities",
|
||||
"titlePart1": "Building",
|
||||
"titleHighlight": "Secure, Scalable",
|
||||
"titlePart2": "Enterprise-Grade Systems",
|
||||
"yearsExp": "3+ Years",
|
||||
"subtitle": "in Banking Technology. Backend Developer specializing in Java Spring Boot, Microservices Architecture, and Enterprise Security.",
|
||||
"ctaContact": "Get in Touch",
|
||||
"ctaProjects": "View Projects",
|
||||
"scroll": "Scroll"
|
||||
},
|
||||
"Experience": {
|
||||
"badge": "Career Journey",
|
||||
"title": "Experience & Evolution",
|
||||
"subtitle": "A timeline of building enterprise-grade systems in the banking technology industry.",
|
||||
"jobs": {
|
||||
"enterprise": {
|
||||
"year": "2024 — Present",
|
||||
"title": "Senior Backend Developer",
|
||||
"company": "Enterprise Banking Corp",
|
||||
"description": "Leading microservices architecture design and implementation for core banking platform.",
|
||||
"achievements": [
|
||||
"Architected event-driven system processing 500K+ transactions/day",
|
||||
"Reduced API response time by 40% through caching optimization",
|
||||
"Mentored team of 4 junior developers"
|
||||
]
|
||||
},
|
||||
"digital": {
|
||||
"year": "2023 — 2024",
|
||||
"title": "Backend Developer",
|
||||
"company": "Digital Banking Solutions",
|
||||
"description": "Developed and maintained Spring Boot microservices for payment processing and customer management.",
|
||||
"achievements": [
|
||||
"Built real-time notification service with Apache Kafka",
|
||||
"Implemented OAuth2 + JWT authentication system",
|
||||
"Achieved 99.9% uptime on production services"
|
||||
]
|
||||
},
|
||||
"fintech": {
|
||||
"year": "2021 — 2023",
|
||||
"title": "Junior Backend Developer",
|
||||
"company": "FinTech Startup",
|
||||
"description": "Started career building REST APIs and database management for financial applications.",
|
||||
"achievements": [
|
||||
"Designed PostgreSQL schema handling 1M+ records",
|
||||
"Created automated CI/CD pipeline with Jenkins",
|
||||
"Developed unit & integration test coverage to 85%"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"TechStack": {
|
||||
"badge": "Tech Arsenal",
|
||||
"title": "Technology Stack",
|
||||
"subtitle": "A comprehensive toolkit forged through years of enterprise development, from backend infrastructure to mobile interfaces.",
|
||||
"categories": {
|
||||
"backend": {
|
||||
"title": "Enterprise Backend",
|
||||
"description": "Core systems & microservices"
|
||||
},
|
||||
"infra": {
|
||||
"title": "Database & Infrastructure",
|
||||
"description": "Data management & DevOps"
|
||||
},
|
||||
"frontend": {
|
||||
"title": "Frontend Development",
|
||||
"description": "Modern web interfaces"
|
||||
},
|
||||
"mobile": {
|
||||
"title": "Mobile Development",
|
||||
"description": "Cross-platform apps"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Projects": {
|
||||
"badge": "Portfolio",
|
||||
"title": "Projects & Case Studies",
|
||||
"subtitle": "Real-world enterprise solutions built for scale, security, and reliability.",
|
||||
"filters": {
|
||||
"all": "All Projects",
|
||||
"backend": "Backend",
|
||||
"frontend": "Frontend",
|
||||
"mobile": "Mobile"
|
||||
},
|
||||
"items": {
|
||||
"apiGateway": {
|
||||
"title": "Core Banking API Gateway",
|
||||
"description": "High-performance API Gateway handling 500K+ daily transactions with rate limiting, circuit breaker pattern, and distributed tracing across 12 microservices.",
|
||||
"metrics": "Increased throughput by 40%, 99.9% uptime"
|
||||
},
|
||||
"paymentEngine": {
|
||||
"title": "Payment Processing Engine",
|
||||
"description": "Event-driven payment system with Saga pattern for distributed transactions, supporting real-time transfers, bill payments, and batch processing.",
|
||||
"metrics": "Processing 200K+ payments/day"
|
||||
},
|
||||
"onboarding": {
|
||||
"title": "Customer Onboarding Portal",
|
||||
"description": "Modern onboarding portal with multi-step KYC verification, document upload, and real-time status tracking for banking customers."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Internal Dashboard",
|
||||
"description": "Admin dashboard for monitoring API performance, user analytics, and system health with real-time WebSocket updates."
|
||||
},
|
||||
"mobileApp": {
|
||||
"title": "Mobile Banking App",
|
||||
"description": "Cross-platform mobile banking application with biometric authentication, push notifications, and offline transaction history."
|
||||
},
|
||||
"authService": {
|
||||
"title": "Authentication Microservice",
|
||||
"description": "Centralized auth service with OAuth2, JWT, MFA, and session management. Supports SSO across 8 internal applications.",
|
||||
"metrics": "Securing 50K+ active users"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Contact": {
|
||||
"badge": "Let's Connect",
|
||||
"title": "Get in Touch",
|
||||
"subtitle": "Interested in working together? Whether you're a recruiter, hiring manager, or potential collaborator — I'd love to hear from you.",
|
||||
"form": {
|
||||
"nameLabel": "Full Name",
|
||||
"namePlaceholder": "John Doe",
|
||||
"emailLabel": "Email Address",
|
||||
"emailPlaceholder": "john@company.com",
|
||||
"messageLabel": "Message",
|
||||
"messagePlaceholder": "Tell me about the opportunity or project you have in mind...",
|
||||
"submit": "Send Message",
|
||||
"submitting": "Sending...",
|
||||
"successTitle": "Message Sent!",
|
||||
"successDesc": "Thank you for reaching out. I'll get back to you soon."
|
||||
}
|
||||
},
|
||||
"Footer": {
|
||||
"backToTop": "Back to top",
|
||||
"copyright": "Yolando. Built with Next.js & crafted with purpose."
|
||||
}
|
||||
}
|
||||
143
messages/id.json
Normal file
143
messages/id.json
Normal file
@@ -0,0 +1,143 @@
|
||||
{
|
||||
"Navigation": {
|
||||
"experience": "Pengalaman",
|
||||
"techStack": "Tech Stack",
|
||||
"projects": "Proyek",
|
||||
"contact": "Kontak"
|
||||
},
|
||||
"Hero": {
|
||||
"badge": "Tersedia untuk peluang baru",
|
||||
"titlePart1": "Membangun Sistem",
|
||||
"titleHighlight": "Aman & Skalabel",
|
||||
"titlePart2": "Skala Enterprise",
|
||||
"yearsExp": "3+ Tahun",
|
||||
"subtitle": "di Teknologi Perbankan. Backend Developer dengan spesialisasi Java Spring Boot, Arsitektur Microservices, dan Keamanan Enterprise.",
|
||||
"ctaContact": "Hubungi Saya",
|
||||
"ctaProjects": "Lihat Proyek",
|
||||
"scroll": "Scroll"
|
||||
},
|
||||
"Experience": {
|
||||
"badge": "Perjalanan Karir",
|
||||
"title": "Pengalaman & Evolusi",
|
||||
"subtitle": "Garis waktu (timeline) membangun sistem skala enterprise di industri teknologi perbankan.",
|
||||
"jobs": {
|
||||
"enterprise": {
|
||||
"year": "2024 — Sekarang",
|
||||
"title": "Senior Backend Developer",
|
||||
"company": "Enterprise Banking Corp",
|
||||
"description": "Memimpin desain dan implementasi arsitektur microservices untuk platform perbankan inti.",
|
||||
"achievements": [
|
||||
"Merancang sistem event-driven yang memproses 500Ribu+ transaksi/hari",
|
||||
"Meningkatkan kecepatan respons API hingga 40% melalui optimasi caching",
|
||||
"Mementori 4 junior developer"
|
||||
]
|
||||
},
|
||||
"digital": {
|
||||
"year": "2023 — 2024",
|
||||
"title": "Backend Developer",
|
||||
"company": "Digital Banking Solutions",
|
||||
"description": "Mengembangkan dan memelihara microservices Spring Boot untuk pemrosesan pembayaran dan manajemen pelanggan.",
|
||||
"achievements": [
|
||||
"Membangun layanan notifikasi real-time dengan Apache Kafka",
|
||||
"Menerapkan sistem autentikasi OAuth2 + JWT",
|
||||
"Mencapai 99.9% uptime pada layanan produksi"
|
||||
]
|
||||
},
|
||||
"fintech": {
|
||||
"year": "2021 — 2023",
|
||||
"title": "Junior Backend Developer",
|
||||
"company": "FinTech Startup",
|
||||
"description": "Memulai karir membangun REST API dan manajemen database untuk aplikasi finansial.",
|
||||
"achievements": [
|
||||
"Mendesain skema PostgreSQL yang menangani 1Juta+ data",
|
||||
"Membuat pipeline CI/CD otomatis dengan Jenkins",
|
||||
"Meningkatkan cakupan unit & integration test hingga 85%"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"TechStack": {
|
||||
"badge": "Arsenal Teknologi",
|
||||
"title": "Tech Stack",
|
||||
"subtitle": "Perangkat komprehensif yang ditempa dari pengalaman pengembangan enterprise, mulai dari infrastruktur backend hingga antarmuka mobile.",
|
||||
"categories": {
|
||||
"backend": {
|
||||
"title": "Enterprise Backend",
|
||||
"description": "Sistem inti & microservices"
|
||||
},
|
||||
"infra": {
|
||||
"title": "Database & Infrastruktur",
|
||||
"description": "Manajemen data & DevOps"
|
||||
},
|
||||
"frontend": {
|
||||
"title": "Frontend Development",
|
||||
"description": "Antarmuka web modern"
|
||||
},
|
||||
"mobile": {
|
||||
"title": "Mobile Development",
|
||||
"description": "Aplikasi cross-platform"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Projects": {
|
||||
"badge": "Portofolio",
|
||||
"title": "Proyek & Studi Kasus",
|
||||
"subtitle": "Solusi enterprise dunia nyata yang dibangun untuk skalabilitas, keamanan, dan keandalan.",
|
||||
"filters": {
|
||||
"all": "Semua Proyek",
|
||||
"backend": "Backend",
|
||||
"frontend": "Frontend",
|
||||
"mobile": "Mobile"
|
||||
},
|
||||
"items": {
|
||||
"apiGateway": {
|
||||
"title": "Core Banking API Gateway",
|
||||
"description": "API Gateway performa tinggi yang menangani 500Ribu+ transaksi harian dengan rate limiting, pola circuit breaker, dan distributed tracing pada 12 microservices.",
|
||||
"metrics": "Meningkatkan throughput 40%, 99.9% uptime"
|
||||
},
|
||||
"paymentEngine": {
|
||||
"title": "Mesin Pemroses Pembayaran",
|
||||
"description": "Sistem pembayaran event-driven dengan pola Saga untuk transaksi terdistribusi, mendukung transfer real-time, pembayaran tagihan, dan pemrosesan batch.",
|
||||
"metrics": "Memproses 200Ribu+ pembayaran/hari"
|
||||
},
|
||||
"onboarding": {
|
||||
"title": "Portal Onboarding Pelanggan",
|
||||
"description": "Portal onboarding modern dengan verifikasi KYC multi-langkah, unggah dokumen, dan pelacakan status real-time untuk pelanggan perbankan."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Dashboard Internal",
|
||||
"description": "Dashboard admin untuk memantau performa API, analitik pengguna, dan kesehatan sistem dengan update WebSocket real-time."
|
||||
},
|
||||
"mobileApp": {
|
||||
"title": "Aplikasi Mobile Banking",
|
||||
"description": "Aplikasi mobile banking cross-platform dengan autentikasi biometrik, notifikasi push, dan riwayat transaksi offline."
|
||||
},
|
||||
"authService": {
|
||||
"title": "Microservice Autentikasi",
|
||||
"description": "Layanan autentikasi terpusat dengan OAuth2, JWT, MFA, dan manajemen sesi. Mendukung SSO di 8 aplikasi internal.",
|
||||
"metrics": "Mengamankan 50Ribu+ pengguna aktif"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Contact": {
|
||||
"badge": "Mari Terhubung",
|
||||
"title": "Hubungi Saya",
|
||||
"subtitle": "Tertarik bekerja sama? Baik Anda seorang recruiter, hiring manager, atau kolaborator potensial — saya sangat ingin mendengarnya.",
|
||||
"form": {
|
||||
"nameLabel": "Nama Lengkap",
|
||||
"namePlaceholder": "Budi Santoso",
|
||||
"emailLabel": "Alamat Email",
|
||||
"emailPlaceholder": "budi@perusahaan.com",
|
||||
"messageLabel": "Pesan",
|
||||
"messagePlaceholder": "Ceritakan tentang peluang atau proyek yang Anda pikirkan...",
|
||||
"submit": "Kirim Pesan",
|
||||
"submitting": "Mengirim...",
|
||||
"successTitle": "Pesan Terkirim!",
|
||||
"successDesc": "Terima kasih telah menghubungi. Saya akan segera membalasnya."
|
||||
}
|
||||
},
|
||||
"Footer": {
|
||||
"backToTop": "Kembali ke atas",
|
||||
"copyright": "Yolando. Dibangun dengan Next.js & dibuat dengan penuh tujuan."
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
import type { NextConfig } from "next";
|
||||
import createNextIntlPlugin from 'next-intl/plugin';
|
||||
|
||||
const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
export default withNextIntl(nextConfig);
|
||||
|
||||
688
package-lock.json
generated
688
package-lock.json
generated
@@ -11,6 +11,7 @@
|
||||
"framer-motion": "^12.38.0",
|
||||
"lucide-react": "^1.7.0",
|
||||
"next": "^15.5.14",
|
||||
"next-intl": "^4.8.3",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4"
|
||||
@@ -203,6 +204,51 @@
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/bigdecimal": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/bigdecimal/-/bigdecimal-0.2.0.tgz",
|
||||
"integrity": "sha512-GeaxHZbUoYvHL9tC5eltHLs+1zU70aPw0s7LwqgktIzF5oMhNY4o4deEtusJMsq7WFJF3Ye2zQEzdG8beVk73w=="
|
||||
},
|
||||
"node_modules/@formatjs/ecma402-abstract": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-3.2.0.tgz",
|
||||
"integrity": "sha512-dHnqHgBo6GXYGRsepaE1wmsC2etaivOWd5VaJstZd+HI2zR3DCUjbDVZRtoPGkkXZmyHvBwrdEUuqfvzhF/DtQ==",
|
||||
"dependencies": {
|
||||
"@formatjs/bigdecimal": "0.2.0",
|
||||
"@formatjs/fast-memoize": "3.1.1",
|
||||
"@formatjs/intl-localematcher": "0.8.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/fast-memoize": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.1.tgz",
|
||||
"integrity": "sha512-CbNbf+tlJn1baRnPkNePnBqTLxGliG6DDgNa/UtV66abwIjwsliPMOt0172tzxABYzSuxZBZfcp//qI8AvBWPg=="
|
||||
},
|
||||
"node_modules/@formatjs/icu-messageformat-parser": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.3.tgz",
|
||||
"integrity": "sha512-HJWZ9S6JWey6iY5+YXE3Kd0ofWU1sC2KTTp56e1168g/xxWvVvr8k9G4fexIgwYV9wbtjY7kGYK5FjoWB3B2OQ==",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "3.2.0",
|
||||
"@formatjs/icu-skeleton-parser": "2.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/icu-skeleton-parser": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.1.3.tgz",
|
||||
"integrity": "sha512-9mFp8TJ166ZM2pcjKwsBWXrDnOJGT7vMEScVgLygUODPOsE8S6f/FHoacvrlHK1B4dYZk8vSCNruyPU64AfgJQ==",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl-localematcher": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.2.tgz",
|
||||
"integrity": "sha512-q05KMYGJLyqFNFtIb8NhWLF5X3aK/k0wYt7dnRFuy6aLQL+vUwQ1cg5cO4qawEiINybeCPXAWlprY2mSBjSXAQ==",
|
||||
"dependencies": {
|
||||
"@formatjs/fast-memoize": "3.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||
@@ -917,6 +963,298 @@
|
||||
"node": ">=12.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz",
|
||||
"integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.3",
|
||||
"is-glob": "^4.0.3",
|
||||
"node-addon-api": "^7.0.0",
|
||||
"picomatch": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@parcel/watcher-android-arm64": "2.5.6",
|
||||
"@parcel/watcher-darwin-arm64": "2.5.6",
|
||||
"@parcel/watcher-darwin-x64": "2.5.6",
|
||||
"@parcel/watcher-freebsd-x64": "2.5.6",
|
||||
"@parcel/watcher-linux-arm-glibc": "2.5.6",
|
||||
"@parcel/watcher-linux-arm-musl": "2.5.6",
|
||||
"@parcel/watcher-linux-arm64-glibc": "2.5.6",
|
||||
"@parcel/watcher-linux-arm64-musl": "2.5.6",
|
||||
"@parcel/watcher-linux-x64-glibc": "2.5.6",
|
||||
"@parcel/watcher-linux-x64-musl": "2.5.6",
|
||||
"@parcel/watcher-win32-arm64": "2.5.6",
|
||||
"@parcel/watcher-win32-ia32": "2.5.6",
|
||||
"@parcel/watcher-win32-x64": "2.5.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-android-arm64": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz",
|
||||
"integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-darwin-arm64": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz",
|
||||
"integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-darwin-x64": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz",
|
||||
"integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-freebsd-x64": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz",
|
||||
"integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm-glibc": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz",
|
||||
"integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm-musl": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz",
|
||||
"integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm64-glibc": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz",
|
||||
"integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm64-musl": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz",
|
||||
"integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-x64-glibc": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz",
|
||||
"integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-x64-musl": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz",
|
||||
"integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-win32-arm64": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz",
|
||||
"integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-win32-ia32": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz",
|
||||
"integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-win32-x64": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz",
|
||||
"integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher/node_modules/picomatch": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/@rtsao/scc": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
||||
@@ -929,6 +1267,196 @@
|
||||
"integrity": "sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@schummar/icu-type-parser": {
|
||||
"version": "1.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz",
|
||||
"integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw=="
|
||||
},
|
||||
"node_modules/@swc/core-darwin-arm64": {
|
||||
"version": "1.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.21.tgz",
|
||||
"integrity": "sha512-SA8SFg9dp0qKRH8goWsax6bptFE2EdmPf2YRAQW9WoHGf3XKM1bX0nd5UdwxmC5hXsBUZAYf7xSciCler6/oyA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-darwin-x64": {
|
||||
"version": "1.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.21.tgz",
|
||||
"integrity": "sha512-//fOVntgowz9+V90lVsNCtyyrtbHp3jWH6Rch7MXHXbcvbLmbCTmssl5DeedUWLLGiAAW1wksBdqdGYOTjaNLw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
||||
"version": "1.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.21.tgz",
|
||||
"integrity": "sha512-meNI4Sh6h9h8DvIfEc0l5URabYMSuNvyisLmG6vnoYAS43s8ON3NJR8sDHvdP7NJTrLe0q/x2XCn6yL/BeHcZg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm64-gnu": {
|
||||
"version": "1.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.21.tgz",
|
||||
"integrity": "sha512-QrXlNQnHeXqU2EzLlnsPoWEh8/GtNJLvfMiPsDhk+ht6Xv8+vhvZ5YZ/BokNWSIZiWPKLAqR0M7T92YF5tmD3g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm64-musl": {
|
||||
"version": "1.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.21.tgz",
|
||||
"integrity": "sha512-8/yGCMO333ultDaMQivE5CjO6oXDPeeg1IV4sphojPkb0Pv0i6zvcRIkgp60xDB+UxLr6VgHgt+BBgqS959E9g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-ppc64-gnu": {
|
||||
"version": "1.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.21.tgz",
|
||||
"integrity": "sha512-ucW0HzPx0s1dgRvcvuLSPSA/2Kk/VYTv9st8qe1Kc22Gu0Q0rH9+6TcBTmMuNIp0Xs4BPr1uBttmbO1wEGI49Q==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-s390x-gnu": {
|
||||
"version": "1.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.21.tgz",
|
||||
"integrity": "sha512-ulTnOGc5I7YRObE/9NreAhQg94QkiR5qNhhcUZ1iFAYjzg/JGAi1ch+s/Ixe61pMIr8bfVrF0NOaB0f8wjaAfA==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-x64-gnu": {
|
||||
"version": "1.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.21.tgz",
|
||||
"integrity": "sha512-D0RokxtM+cPvSqJIKR6uja4hbD+scI9ezo95mBhfSyLUs9wnPPl26sLp1ZPR/EXRdYm3F3S6RUtVi+8QXhT24Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-x64-musl": {
|
||||
"version": "1.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.21.tgz",
|
||||
"integrity": "sha512-nER8u7VeRfmU6fMDzl1NQAbbB/G7O2avmvCOwIul1uGkZ2/acbPH+DCL9h5+0yd/coNcxMBTL6NGepIew+7C2w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-win32-arm64-msvc": {
|
||||
"version": "1.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.21.tgz",
|
||||
"integrity": "sha512-+/AgNBnjYugUA8C0Do4YzymgvnGbztv7j8HKSQLvR/DQgZPoXQ2B3PqB2mTtGh/X5DhlJWiqnunN35JUgWcAeQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-win32-ia32-msvc": {
|
||||
"version": "1.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.21.tgz",
|
||||
"integrity": "sha512-IkSZj8PX/N4HcaFhMQtzmkV8YSnuNoJ0E6OvMwFiOfejPhiKXvl7CdDsn1f4/emYEIDO3fpgZW9DTaCRMDxaDA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-win32-x64-msvc": {
|
||||
"version": "1.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.21.tgz",
|
||||
"integrity": "sha512-zUyWso7OOENB6e1N1hNuNn8vbvLsTdKQ5WKLgt/JcBNfJhKy/6jmBmqI3GXk/MyvQKd5SLvP7A0F36p7TeDqvw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/counter": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
||||
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.15",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
|
||||
@@ -937,6 +1465,14 @@
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/types": {
|
||||
"version": "0.1.26",
|
||||
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.26.tgz",
|
||||
"integrity": "sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==",
|
||||
"dependencies": {
|
||||
"@swc/counter": "^0.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tybys/wasm-util": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
||||
@@ -2246,7 +2782,6 @@
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -3335,6 +3870,20 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/icu-minify": {
|
||||
"version": "4.8.3",
|
||||
"resolved": "https://registry.npmjs.org/icu-minify/-/icu-minify-4.8.3.tgz",
|
||||
"integrity": "sha512-65Av7FLosNk7bPbmQx5z5XG2Y3T2GFppcjiXh4z1idHeVgQxlDpAmkGoYI0eFzAvrOnjpWTL5FmPDhsdfRMPEA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/amannn"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@formatjs/icu-messageformat-parser": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
@@ -3383,6 +3932,16 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/intl-messageformat": {
|
||||
"version": "11.2.0",
|
||||
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-11.2.0.tgz",
|
||||
"integrity": "sha512-IhghAA8n4KSlXuWKzYsWyWb82JoYTzShfyvdSF85oJPnNOjvv4kAo7S7Jtkm3/vJ53C7dQNRO+Gpnj3iWgTjBQ==",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "3.2.0",
|
||||
"@formatjs/fast-memoize": "3.1.1",
|
||||
"@formatjs/icu-messageformat-parser": "3.5.3"
|
||||
}
|
||||
},
|
||||
"node_modules/is-array-buffer": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
|
||||
@@ -3535,7 +4094,6 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -3578,7 +4136,6 @@
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
},
|
||||
@@ -4091,6 +4648,14 @@
|
||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
||||
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "15.5.14",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-15.5.14.tgz",
|
||||
@@ -4142,6 +4707,91 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/next-intl": {
|
||||
"version": "4.8.3",
|
||||
"resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.8.3.tgz",
|
||||
"integrity": "sha512-PvdBDWg+Leh7BR7GJUQbCDVVaBRn37GwDBWc9sv0rVQOJDQ5JU1rVzx9EEGuOGYo0DHAl70++9LQ7HxTawdL7w==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/amannn"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@formatjs/intl-localematcher": "^0.8.1",
|
||||
"@parcel/watcher": "^2.4.1",
|
||||
"@swc/core": "^1.15.2",
|
||||
"icu-minify": "^4.8.3",
|
||||
"negotiator": "^1.0.0",
|
||||
"next-intl-swc-plugin-extractor": "^4.8.3",
|
||||
"po-parser": "^2.1.1",
|
||||
"use-intl": "^4.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/next-intl-swc-plugin-extractor": {
|
||||
"version": "4.8.3",
|
||||
"resolved": "https://registry.npmjs.org/next-intl-swc-plugin-extractor/-/next-intl-swc-plugin-extractor-4.8.3.tgz",
|
||||
"integrity": "sha512-YcaT+R9z69XkGhpDarVFWUprrCMbxgIQYPUaXoE6LGVnLjGdo8hu3gL6bramDVjNKViYY8a/pXPy7Bna0mXORg=="
|
||||
},
|
||||
"node_modules/next-intl/node_modules/@swc/core": {
|
||||
"version": "1.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.21.tgz",
|
||||
"integrity": "sha512-fkk7NJcBscrR3/F8jiqlMptRHP650NxqDnspBMrRe5d8xOoCy9MLL5kOBLFXjFLfMo3KQQHhk+/jUULOMlR1uQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@swc/counter": "^0.1.3",
|
||||
"@swc/types": "^0.1.25"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/swc"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@swc/core-darwin-arm64": "1.15.21",
|
||||
"@swc/core-darwin-x64": "1.15.21",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.15.21",
|
||||
"@swc/core-linux-arm64-gnu": "1.15.21",
|
||||
"@swc/core-linux-arm64-musl": "1.15.21",
|
||||
"@swc/core-linux-ppc64-gnu": "1.15.21",
|
||||
"@swc/core-linux-s390x-gnu": "1.15.21",
|
||||
"@swc/core-linux-x64-gnu": "1.15.21",
|
||||
"@swc/core-linux-x64-musl": "1.15.21",
|
||||
"@swc/core-win32-arm64-msvc": "1.15.21",
|
||||
"@swc/core-win32-ia32-msvc": "1.15.21",
|
||||
"@swc/core-win32-x64-msvc": "1.15.21"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc/helpers": ">=0.5.17"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@swc/helpers": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/next-intl/node_modules/@swc/helpers": {
|
||||
"version": "0.5.20",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.20.tgz",
|
||||
"integrity": "sha512-2egEBHUMasdypIzrprsu8g+OEVd7Vp2MM3a2eVlM/cyFYto0nGz5BX5BTgh/ShZZI9ed+ozEq+Ngt+rgmUs8tw==",
|
||||
"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",
|
||||
@@ -4178,6 +4828,11 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
|
||||
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="
|
||||
},
|
||||
"node_modules/node-exports-info": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz",
|
||||
@@ -4479,6 +5134,11 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/po-parser": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/po-parser/-/po-parser-2.1.1.tgz",
|
||||
"integrity": "sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ=="
|
||||
},
|
||||
"node_modules/possible-typed-array-names": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
|
||||
@@ -5595,7 +6255,7 @@
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -5701,6 +6361,26 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-intl": {
|
||||
"version": "4.8.3",
|
||||
"resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.8.3.tgz",
|
||||
"integrity": "sha512-nLxlC/RH+le6g3amA508Itnn/00mE+J22ui21QhOWo5V9hCEC43+WtnRAITbJW0ztVZphev5X9gvOf2/Dk9PLA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/amannn"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@formatjs/fast-memoize": "^3.1.0",
|
||||
"@schummar/icu-type-parser": "1.21.5",
|
||||
"icu-minify": "^4.8.3",
|
||||
"intl-messageformat": "^11.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"framer-motion": "^12.38.0",
|
||||
"lucide-react": "^1.7.0",
|
||||
"next": "^15.5.14",
|
||||
"next-intl": "^4.8.3",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4"
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import { ThemeProvider } from "@/shared/components/theme-provider";
|
||||
import "./globals.css";
|
||||
import { NextIntlClientProvider } from "next-intl";
|
||||
import { getMessages } from "next-intl/server";
|
||||
import { notFound } from "next/navigation";
|
||||
import { routing } from "@/i18n/routing";
|
||||
import "../globals.css";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
@@ -35,19 +39,34 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
params,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
params: Promise<{ locale: string }>;
|
||||
}>) {
|
||||
const { locale } = await params;
|
||||
|
||||
// Ensure that the incoming `locale` is valid
|
||||
if (!routing.locales.includes(locale as any)) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Providing all messages to the client
|
||||
// side is the easiest way to get started
|
||||
const messages = await getMessages();
|
||||
|
||||
return (
|
||||
<html
|
||||
lang="en"
|
||||
lang={locale}
|
||||
suppressHydrationWarning
|
||||
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
|
||||
>
|
||||
<body className="min-h-full flex flex-col">
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
<ThemeProvider>{children}</ThemeProvider>
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
@@ -3,67 +3,58 @@
|
||||
import { AnimatedSection } from "@/shared/components/animated-section";
|
||||
import { SectionHeading } from "@/shared/components/section-heading";
|
||||
import { Briefcase, Award, Rocket, Code2, Building2 } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
interface TimelineItem {
|
||||
year: string;
|
||||
title: string;
|
||||
company: string;
|
||||
description: string;
|
||||
achievements: string[];
|
||||
icon: React.ReactNode;
|
||||
}
|
||||
export function ExperienceSection() {
|
||||
const t = useTranslations("Experience");
|
||||
|
||||
const timelineData: TimelineItem[] = [
|
||||
const timelineData = [
|
||||
{
|
||||
year: "2024 — Present",
|
||||
title: "Senior Backend Developer",
|
||||
company: "Enterprise Banking Corp",
|
||||
description:
|
||||
"Leading microservices architecture design and implementation for core banking platform.",
|
||||
year: t("jobs.enterprise.year"),
|
||||
title: t("jobs.enterprise.title"),
|
||||
company: t("jobs.enterprise.company"),
|
||||
description: t("jobs.enterprise.description"),
|
||||
achievements: [
|
||||
"Architected event-driven system processing 500K+ transactions/day",
|
||||
"Reduced API response time by 40% through caching optimization",
|
||||
"Mentored team of 4 junior developers",
|
||||
t("jobs.enterprise.achievements.0"),
|
||||
t("jobs.enterprise.achievements.1"),
|
||||
t("jobs.enterprise.achievements.2"),
|
||||
],
|
||||
icon: <Rocket size={20} />,
|
||||
},
|
||||
{
|
||||
year: "2023 — 2024",
|
||||
title: "Backend Developer",
|
||||
company: "Digital Banking Solutions",
|
||||
description:
|
||||
"Developed and maintained Spring Boot microservices for payment processing and customer management.",
|
||||
year: t("jobs.digital.year"),
|
||||
title: t("jobs.digital.title"),
|
||||
company: t("jobs.digital.company"),
|
||||
description: t("jobs.digital.description"),
|
||||
achievements: [
|
||||
"Built real-time notification service with Apache Kafka",
|
||||
"Implemented OAuth2 + JWT authentication system",
|
||||
"Achieved 99.9% uptime on production services",
|
||||
t("jobs.digital.achievements.0"),
|
||||
t("jobs.digital.achievements.1"),
|
||||
t("jobs.digital.achievements.2"),
|
||||
],
|
||||
icon: <Code2 size={20} />,
|
||||
},
|
||||
{
|
||||
year: "2021 — 2023",
|
||||
title: "Junior Backend Developer",
|
||||
company: "FinTech Startup",
|
||||
description:
|
||||
"Started career building REST APIs and database management for financial applications.",
|
||||
year: t("jobs.fintech.year"),
|
||||
title: t("jobs.fintech.title"),
|
||||
company: t("jobs.fintech.company"),
|
||||
description: t("jobs.fintech.description"),
|
||||
achievements: [
|
||||
"Designed PostgreSQL schema handling 1M+ records",
|
||||
"Created automated CI/CD pipeline with Jenkins",
|
||||
"Developed unit & integration test coverage to 85%",
|
||||
t("jobs.fintech.achievements.0"),
|
||||
t("jobs.fintech.achievements.1"),
|
||||
t("jobs.fintech.achievements.2"),
|
||||
],
|
||||
icon: <Building2 size={20} />,
|
||||
},
|
||||
];
|
||||
|
||||
export function ExperienceSection() {
|
||||
return (
|
||||
<section id="experience" className="section-padding relative">
|
||||
<div className="max-w-5xl mx-auto px-6">
|
||||
<AnimatedSection>
|
||||
<SectionHeading
|
||||
badge="Career Journey"
|
||||
title="Experience & Evolution"
|
||||
subtitle="A timeline of building enterprise-grade systems in the banking technology industry."
|
||||
badge={t("badge")}
|
||||
title={t("title")}
|
||||
subtitle={t("subtitle")}
|
||||
/>
|
||||
</AnimatedSection>
|
||||
|
||||
|
||||
@@ -2,23 +2,23 @@
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
import { ArrowDown, FileText, Send } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export function HeroSection() {
|
||||
const t = useTranslations("Hero");
|
||||
|
||||
return (
|
||||
<section
|
||||
id="hero"
|
||||
className="relative min-h-screen flex items-center justify-center overflow-hidden"
|
||||
>
|
||||
{/* Background effects */}
|
||||
<div className="absolute inset-0 grid-pattern opacity-30" />
|
||||
|
||||
{/* Gradient orbs */}
|
||||
<div className="absolute top-1/4 -left-32 w-96 h-96 rounded-full bg-accent/20 blur-[120px] animate-pulse-glow" />
|
||||
<div className="absolute bottom-1/4 -right-32 w-96 h-96 rounded-full bg-purple-500/15 blur-[120px] animate-pulse-glow" style={{ animationDelay: "2s" }} />
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] rounded-full bg-indigo-500/5 blur-[100px]" />
|
||||
|
||||
<div className="relative z-10 max-w-5xl mx-auto px-6 text-center">
|
||||
{/* Status badge */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
@@ -29,37 +29,32 @@ export function HeroSection() {
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-success opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-success" />
|
||||
</span>
|
||||
Available for opportunities
|
||||
{t("badge")}
|
||||
</span>
|
||||
</motion.div>
|
||||
|
||||
{/* Main heading */}
|
||||
<motion.h1
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.7, delay: 0.3 }}
|
||||
className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold tracking-tight leading-[1.1] mb-6"
|
||||
>
|
||||
Building{" "}
|
||||
<span className="gradient-text">Secure, Scalable</span>
|
||||
{t("titlePart1")}{" "}
|
||||
<span className="gradient-text">{t("titleHighlight")}</span>
|
||||
<br />
|
||||
Enterprise-Grade Systems
|
||||
{t("titlePart2")}
|
||||
</motion.h1>
|
||||
|
||||
{/* Subtitle */}
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.7, delay: 0.5 }}
|
||||
className="text-lg md:text-xl text-muted-foreground max-w-2xl mx-auto mb-10 leading-relaxed"
|
||||
>
|
||||
<span className="font-mono text-accent font-semibold">3+ Years</span>{" "}
|
||||
in Banking Technology.{" "}
|
||||
<span className="text-foreground">Backend Developer</span> specializing in
|
||||
Java Spring Boot, Microservices Architecture, and Enterprise Security.
|
||||
<span className="font-mono text-accent font-semibold">{t("yearsExp")}</span>{" "}
|
||||
{t("subtitle")}
|
||||
</motion.p>
|
||||
|
||||
{/* CTA buttons */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
@@ -71,18 +66,17 @@ export function HeroSection() {
|
||||
className="group inline-flex items-center gap-2 px-7 py-3.5 rounded-xl bg-gradient-to-r from-accent to-purple-500 text-white font-semibold text-sm shadow-lg shadow-accent/25 hover:shadow-accent/40 hover:scale-105 transition-all duration-300"
|
||||
>
|
||||
<Send size={16} />
|
||||
Get in Touch
|
||||
{t("ctaContact")}
|
||||
</a>
|
||||
<a
|
||||
href="#projects"
|
||||
className="inline-flex items-center gap-2 px-7 py-3.5 rounded-xl font-semibold text-sm border border-border hover:bg-muted/50 transition-all duration-300 hover:scale-105"
|
||||
>
|
||||
<FileText size={16} />
|
||||
View Projects
|
||||
{t("ctaProjects")}
|
||||
</a>
|
||||
</motion.div>
|
||||
|
||||
{/* Tech badges */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
@@ -100,7 +94,6 @@ export function HeroSection() {
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Scroll indicator */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
@@ -108,7 +101,7 @@ export function HeroSection() {
|
||||
className="absolute bottom-8 left-1/2 -translate-x-1/2"
|
||||
>
|
||||
<a href="#experience" className="flex flex-col items-center gap-2 text-muted-foreground hover:text-accent transition-colors">
|
||||
<span className="text-xs font-mono">Scroll</span>
|
||||
<span className="text-xs font-mono">{t("scroll")}</span>
|
||||
<motion.div
|
||||
animate={{ y: [0, 8, 0] }}
|
||||
transition={{ duration: 1.5, repeat: Infinity }}
|
||||
|
||||
@@ -4,8 +4,10 @@ import { useState } from "react";
|
||||
import { AnimatedSection } from "@/shared/components/animated-section";
|
||||
import { SectionHeading } from "@/shared/components/section-heading";
|
||||
import { Send, Mail, User, MessageSquare, CheckCircle, Loader2 } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export function ContactSection() {
|
||||
const t = useTranslations("Contact");
|
||||
const [formState, setFormState] = useState<"idle" | "loading" | "success">("idle");
|
||||
const [formData, setFormData] = useState({
|
||||
name: "",
|
||||
@@ -34,9 +36,9 @@ export function ContactSection() {
|
||||
<div className="relative max-w-4xl mx-auto px-6">
|
||||
<AnimatedSection>
|
||||
<SectionHeading
|
||||
badge="Let's Connect"
|
||||
title="Get in Touch"
|
||||
subtitle="Interested in working together? Whether you're a recruiter, hiring manager, or potential collaborator — I'd love to hear from you."
|
||||
badge={t("badge")}
|
||||
title={t("title")}
|
||||
subtitle={t("subtitle")}
|
||||
/>
|
||||
</AnimatedSection>
|
||||
|
||||
@@ -49,9 +51,9 @@ export function ContactSection() {
|
||||
<div className="w-16 h-16 rounded-full bg-success/10 flex items-center justify-center mx-auto mb-4">
|
||||
<CheckCircle size={32} className="text-success" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold mb-2">Message Sent!</h3>
|
||||
<h3 className="text-xl font-bold mb-2">{t("form.successTitle")}</h3>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
Thank you for reaching out. I'll get back to you soon.
|
||||
{t("form.successDesc")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -66,7 +68,7 @@ export function ContactSection() {
|
||||
className="flex items-center gap-2 text-sm font-medium"
|
||||
>
|
||||
<User size={14} className="text-accent" />
|
||||
Full Name
|
||||
{t("form.nameLabel")}
|
||||
</label>
|
||||
<input
|
||||
id="contact-name"
|
||||
@@ -76,7 +78,7 @@ export function ContactSection() {
|
||||
onChange={(e) =>
|
||||
setFormData((prev) => ({ ...prev, name: e.target.value }))
|
||||
}
|
||||
placeholder="John Doe"
|
||||
placeholder={t("form.namePlaceholder")}
|
||||
className="w-full px-4 py-3 rounded-xl bg-muted/50 border border-border/50 text-sm placeholder:text-muted-foreground/50 focus:outline-none focus:ring-2 focus:ring-accent/50 focus:border-accent/50 transition-all"
|
||||
/>
|
||||
</div>
|
||||
@@ -88,7 +90,7 @@ export function ContactSection() {
|
||||
className="flex items-center gap-2 text-sm font-medium"
|
||||
>
|
||||
<Mail size={14} className="text-accent" />
|
||||
Email Address
|
||||
{t("form.emailLabel")}
|
||||
</label>
|
||||
<input
|
||||
id="contact-email"
|
||||
@@ -98,7 +100,7 @@ export function ContactSection() {
|
||||
onChange={(e) =>
|
||||
setFormData((prev) => ({ ...prev, email: e.target.value }))
|
||||
}
|
||||
placeholder="john@company.com"
|
||||
placeholder={t("form.emailPlaceholder")}
|
||||
className="w-full px-4 py-3 rounded-xl bg-muted/50 border border-border/50 text-sm placeholder:text-muted-foreground/50 focus:outline-none focus:ring-2 focus:ring-accent/50 focus:border-accent/50 transition-all"
|
||||
/>
|
||||
</div>
|
||||
@@ -111,7 +113,7 @@ export function ContactSection() {
|
||||
className="flex items-center gap-2 text-sm font-medium"
|
||||
>
|
||||
<MessageSquare size={14} className="text-accent" />
|
||||
Message
|
||||
{t("form.messageLabel")}
|
||||
</label>
|
||||
<textarea
|
||||
id="contact-message"
|
||||
@@ -121,7 +123,7 @@ export function ContactSection() {
|
||||
onChange={(e) =>
|
||||
setFormData((prev) => ({ ...prev, message: e.target.value }))
|
||||
}
|
||||
placeholder="Tell me about the opportunity or project you have in mind..."
|
||||
placeholder={t("form.messagePlaceholder")}
|
||||
className="w-full px-4 py-3 rounded-xl bg-muted/50 border border-border/50 text-sm placeholder:text-muted-foreground/50 focus:outline-none focus:ring-2 focus:ring-accent/50 focus:border-accent/50 transition-all resize-none"
|
||||
/>
|
||||
</div>
|
||||
@@ -135,12 +137,12 @@ export function ContactSection() {
|
||||
{formState === "loading" ? (
|
||||
<>
|
||||
<Loader2 size={16} className="animate-spin" />
|
||||
Sending...
|
||||
{t("form.submitting")}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Send size={16} />
|
||||
Send Message
|
||||
{t("form.submit")}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
@@ -13,42 +13,33 @@ import {
|
||||
Globe,
|
||||
Layers,
|
||||
} from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
interface Project {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
category: "backend" | "frontend" | "mobile";
|
||||
tags: string[];
|
||||
metrics?: string;
|
||||
repoUrl?: string;
|
||||
liveUrl?: string;
|
||||
}
|
||||
export function ProjectsSection() {
|
||||
const t = useTranslations("Projects");
|
||||
const [activeFilter, setActiveFilter] = useState("all");
|
||||
|
||||
const projects: Project[] = [
|
||||
const projects = [
|
||||
{
|
||||
id: "1",
|
||||
title: "Core Banking API Gateway",
|
||||
description:
|
||||
"High-performance API Gateway handling 500K+ daily transactions with rate limiting, circuit breaker pattern, and distributed tracing across 12 microservices.",
|
||||
title: t("items.apiGateway.title"),
|
||||
description: t("items.apiGateway.description"),
|
||||
category: "backend",
|
||||
tags: ["Spring Boot", "Kafka", "Redis", "Docker"],
|
||||
metrics: "Increased throughput by 40%, 99.9% uptime",
|
||||
metrics: t.has("items.apiGateway.metrics") ? t("items.apiGateway.metrics") : undefined,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
title: "Payment Processing Engine",
|
||||
description:
|
||||
"Event-driven payment system with Saga pattern for distributed transactions, supporting real-time transfers, bill payments, and batch processing.",
|
||||
title: t("items.paymentEngine.title"),
|
||||
description: t("items.paymentEngine.description"),
|
||||
category: "backend",
|
||||
tags: ["Java", "Kafka", "PostgreSQL", "gRPC"],
|
||||
metrics: "Processing 200K+ payments/day",
|
||||
metrics: t.has("items.paymentEngine.metrics") ? t("items.paymentEngine.metrics") : undefined,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
title: "Customer Onboarding Portal",
|
||||
description:
|
||||
"Modern onboarding portal with multi-step KYC verification, document upload, and real-time status tracking for banking customers.",
|
||||
title: t("items.onboarding.title"),
|
||||
description: t("items.onboarding.description"),
|
||||
category: "frontend",
|
||||
tags: ["React", "Next.js", "TypeScript", "Tailwind"],
|
||||
repoUrl: "#",
|
||||
@@ -56,42 +47,36 @@ const projects: Project[] = [
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
title: "Internal Dashboard",
|
||||
description:
|
||||
"Admin dashboard for monitoring API performance, user analytics, and system health with real-time WebSocket updates.",
|
||||
title: t("items.dashboard.title"),
|
||||
description: t("items.dashboard.description"),
|
||||
category: "frontend",
|
||||
tags: ["React", "Chart.js", "WebSocket", "REST"],
|
||||
repoUrl: "#",
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
title: "Mobile Banking App",
|
||||
description:
|
||||
"Cross-platform mobile banking application with biometric authentication, push notifications, and offline transaction history.",
|
||||
title: t("items.mobileApp.title"),
|
||||
description: t("items.mobileApp.description"),
|
||||
category: "mobile",
|
||||
tags: ["React Native", "TypeScript", "Redux"],
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
title: "Authentication Microservice",
|
||||
description:
|
||||
"Centralized auth service with OAuth2, JWT, MFA, and session management. Supports SSO across 8 internal applications.",
|
||||
title: t("items.authService.title"),
|
||||
description: t("items.authService.description"),
|
||||
category: "backend",
|
||||
tags: ["Spring Security", "OAuth2", "JWT", "Redis"],
|
||||
metrics: "Securing 50K+ active users",
|
||||
metrics: t.has("items.authService.metrics") ? t("items.authService.metrics") : undefined,
|
||||
},
|
||||
];
|
||||
|
||||
const filters = [
|
||||
{ value: "all", label: "All Projects", icon: <Layers size={16} /> },
|
||||
{ value: "backend", label: "Backend", icon: <Server size={16} /> },
|
||||
{ value: "frontend", label: "Frontend", icon: <Globe size={16} /> },
|
||||
{ value: "mobile", label: "Mobile", icon: <Smartphone size={16} /> },
|
||||
{ value: "all", label: t("filters.all"), icon: <Layers size={16} /> },
|
||||
{ value: "backend", label: t("filters.backend"), icon: <Server size={16} /> },
|
||||
{ value: "frontend", label: t("filters.frontend"), icon: <Globe size={16} /> },
|
||||
{ value: "mobile", label: t("filters.mobile"), icon: <Smartphone size={16} /> },
|
||||
];
|
||||
|
||||
export function ProjectsSection() {
|
||||
const [activeFilter, setActiveFilter] = useState("all");
|
||||
|
||||
const filteredProjects =
|
||||
activeFilter === "all"
|
||||
? projects
|
||||
@@ -102,9 +87,9 @@ export function ProjectsSection() {
|
||||
<div className="max-w-6xl mx-auto px-6">
|
||||
<AnimatedSection>
|
||||
<SectionHeading
|
||||
badge="Portfolio"
|
||||
title="Projects & Case Studies"
|
||||
subtitle="Real-world enterprise solutions built for scale, security, and reliability."
|
||||
badge={t("badge")}
|
||||
title={t("title")}
|
||||
subtitle={t("subtitle")}
|
||||
/>
|
||||
</AnimatedSection>
|
||||
|
||||
|
||||
@@ -16,23 +16,15 @@ import {
|
||||
Cloud,
|
||||
Workflow,
|
||||
} from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
interface TechItem {
|
||||
name: string;
|
||||
icon: React.ReactNode;
|
||||
}
|
||||
export function TechStackSection() {
|
||||
const t = useTranslations("TechStack");
|
||||
|
||||
interface TechCategory {
|
||||
title: string;
|
||||
description: string;
|
||||
items: TechItem[];
|
||||
accent: string;
|
||||
}
|
||||
|
||||
const techCategories: TechCategory[] = [
|
||||
const techCategories = [
|
||||
{
|
||||
title: "Enterprise Backend",
|
||||
description: "Core systems & microservices",
|
||||
title: t("categories.backend.title"),
|
||||
description: t("categories.backend.description"),
|
||||
accent: "from-blue-500 to-cyan-500",
|
||||
items: [
|
||||
{ name: "Java", icon: <Cpu size={22} /> },
|
||||
@@ -44,8 +36,8 @@ const techCategories: TechCategory[] = [
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Database & Infrastructure",
|
||||
description: "Data management & DevOps",
|
||||
title: t("categories.infra.title"),
|
||||
description: t("categories.infra.description"),
|
||||
accent: "from-emerald-500 to-teal-500",
|
||||
items: [
|
||||
{ name: "PostgreSQL", icon: <Database size={22} /> },
|
||||
@@ -57,8 +49,8 @@ const techCategories: TechCategory[] = [
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Frontend Development",
|
||||
description: "Modern web interfaces",
|
||||
title: t("categories.frontend.title"),
|
||||
description: t("categories.frontend.description"),
|
||||
accent: "from-violet-500 to-purple-500",
|
||||
items: [
|
||||
{ name: "React / Next.js", icon: <Globe size={22} /> },
|
||||
@@ -68,8 +60,8 @@ const techCategories: TechCategory[] = [
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Mobile Development",
|
||||
description: "Cross-platform apps",
|
||||
title: t("categories.mobile.title"),
|
||||
description: t("categories.mobile.description"),
|
||||
accent: "from-orange-500 to-rose-500",
|
||||
items: [
|
||||
{ name: "React Native", icon: <Smartphone size={22} /> },
|
||||
@@ -78,21 +70,19 @@ const techCategories: TechCategory[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export function TechStackSection() {
|
||||
return (
|
||||
<section
|
||||
id="tech-stack"
|
||||
className="section-padding relative bg-muted/30"
|
||||
>
|
||||
{/* Subtle background */}
|
||||
<div className="absolute inset-0 grid-pattern opacity-20" />
|
||||
|
||||
<div className="relative max-w-6xl mx-auto px-6">
|
||||
<AnimatedSection>
|
||||
<SectionHeading
|
||||
badge="Tech Arsenal"
|
||||
title="Technology Stack"
|
||||
subtitle="A comprehensive toolkit forged through years of enterprise development, from backend infrastructure to mobile interfaces."
|
||||
badge={t("badge")}
|
||||
title={t("title")}
|
||||
subtitle={t("subtitle")}
|
||||
/>
|
||||
</AnimatedSection>
|
||||
|
||||
@@ -100,7 +90,6 @@ export function TechStackSection() {
|
||||
{techCategories.map((category, catIndex) => (
|
||||
<AnimatedSection key={category.title} delay={catIndex * 0.1}>
|
||||
<div className="group relative h-full p-6 rounded-2xl bg-card border border-border/50 hover:border-accent/30 transition-all duration-500 hover:shadow-lg">
|
||||
{/* Category header */}
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div
|
||||
className={`w-10 h-10 rounded-xl bg-gradient-to-br ${category.accent} flex items-center justify-center text-white shadow-lg`}
|
||||
@@ -115,7 +104,6 @@ export function TechStackSection() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tech items grid */}
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-2">
|
||||
{category.items.map((tech) => (
|
||||
<div
|
||||
|
||||
15
src/i18n/request.ts
Normal file
15
src/i18n/request.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import {getRequestConfig} from 'next-intl/server';
|
||||
import {routing} from './routing';
|
||||
|
||||
export default getRequestConfig(async ({requestLocale}) => {
|
||||
let locale = await requestLocale;
|
||||
|
||||
if (!locale || !routing.locales.includes(locale as any)) {
|
||||
locale = routing.defaultLocale;
|
||||
}
|
||||
|
||||
return {
|
||||
locale,
|
||||
messages: (await import(`../../messages/${locale}.json`)).default
|
||||
};
|
||||
});
|
||||
10
src/i18n/routing.ts
Normal file
10
src/i18n/routing.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import {defineRouting} from 'next-intl/routing';
|
||||
import {createNavigation} from 'next-intl/navigation';
|
||||
|
||||
export const routing = defineRouting({
|
||||
locales: ['id', 'en'],
|
||||
defaultLocale: 'id',
|
||||
localePrefix: 'as-needed' // Don't show /id for the default locale
|
||||
});
|
||||
|
||||
export const {Link, redirect, usePathname, useRouter, getPathname} = createNavigation(routing);
|
||||
9
src/middleware.ts
Normal file
9
src/middleware.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import createMiddleware from 'next-intl/middleware';
|
||||
import {routing} from './i18n/routing';
|
||||
|
||||
export default createMiddleware(routing);
|
||||
|
||||
export const config = {
|
||||
// Match only internationalized pathnames
|
||||
matcher: ['/', '/(id|en)/:path*']
|
||||
};
|
||||
@@ -1,6 +1,9 @@
|
||||
import { GitFork, Link2, Mail, ArrowUp } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export function Footer() {
|
||||
const t = useTranslations("Footer");
|
||||
|
||||
return (
|
||||
<footer className="relative border-t border-border/50 bg-card/50">
|
||||
<div className="max-w-6xl mx-auto px-6 py-12">
|
||||
@@ -49,7 +52,7 @@ export function Footer() {
|
||||
href="#"
|
||||
className="flex items-center gap-2 text-sm text-muted-foreground hover:text-accent transition-colors group"
|
||||
>
|
||||
Back to top
|
||||
{t("backToTop")}
|
||||
<ArrowUp
|
||||
size={14}
|
||||
className="transition-transform group-hover:-translate-y-0.5"
|
||||
@@ -59,8 +62,7 @@ export function Footer() {
|
||||
|
||||
<div className="mt-8 pt-6 border-t border-border/30 text-center">
|
||||
<p className="text-sm text-muted-foreground font-mono">
|
||||
© {new Date().getFullYear()} Yolando. Built with Next.js & crafted
|
||||
with purpose.
|
||||
© {new Date().getFullYear()} {t("copyright")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState, useEffect, useTransition } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { Menu, X } from "lucide-react";
|
||||
import { Menu, X, Languages } from "lucide-react";
|
||||
import { ThemeToggle } from "@/shared/components/theme-toggle";
|
||||
|
||||
const navLinks = [
|
||||
{ href: "#experience", label: "Experience" },
|
||||
{ href: "#tech-stack", label: "Tech Stack" },
|
||||
{ href: "#projects", label: "Projects" },
|
||||
{ href: "#contact", label: "Contact" },
|
||||
];
|
||||
import { useTranslations, useLocale } from "next-intl";
|
||||
import { usePathname, useRouter } from "@/i18n/routing";
|
||||
|
||||
export function Navbar() {
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const t = useTranslations("Navigation");
|
||||
const locale = useLocale();
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => setIsScrolled(window.scrollY > 20);
|
||||
@@ -22,6 +22,20 @@ export function Navbar() {
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
}, []);
|
||||
|
||||
const navLinks = [
|
||||
{ href: "#experience", label: t("experience") },
|
||||
{ href: "#tech-stack", label: t("techStack") },
|
||||
{ href: "#projects", label: t("projects") },
|
||||
{ href: "#contact", label: t("contact") },
|
||||
];
|
||||
|
||||
const switchLocale = (newLocale: string) => {
|
||||
startTransition(() => {
|
||||
router.replace(pathname, { locale: newLocale });
|
||||
setIsOpen(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.header
|
||||
initial={{ y: -100 }}
|
||||
@@ -56,7 +70,33 @@ export function Navbar() {
|
||||
<span className="absolute bottom-0 left-1/2 -translate-x-1/2 w-0 h-0.5 bg-accent rounded-full transition-all duration-300 group-hover:w-6" />
|
||||
</a>
|
||||
))}
|
||||
<div className="ml-4 pl-4 border-l border-border/50">
|
||||
<div className="ml-4 pl-4 border-l border-border/50 flex items-center gap-2">
|
||||
{/* Language Switcher */}
|
||||
<div className="flex items-center gap-1 bg-muted/50 p-1 rounded-xl border border-border/50">
|
||||
<button
|
||||
disabled={isPending}
|
||||
onClick={() => switchLocale("id")}
|
||||
className={`px-2 py-1 text-xs font-bold rounded-lg transition-colors ${
|
||||
locale === "id"
|
||||
? "bg-background shadow-sm text-foreground"
|
||||
: "text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
ID
|
||||
</button>
|
||||
<button
|
||||
disabled={isPending}
|
||||
onClick={() => switchLocale("en")}
|
||||
className={`px-2 py-1 text-xs font-bold rounded-lg transition-colors ${
|
||||
locale === "en"
|
||||
? "bg-background shadow-sm text-foreground"
|
||||
: "text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
EN
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</div>
|
||||
@@ -94,6 +134,33 @@ export function Navbar() {
|
||||
{link.label}
|
||||
</a>
|
||||
))}
|
||||
<div className="px-4 py-3 mt-2 border-t border-border/30 flex items-center gap-4">
|
||||
<span className="text-sm font-medium flex items-center gap-2 text-muted-foreground">
|
||||
<Languages size={16} /> Language
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={() => switchLocale("id")}
|
||||
className={`px-3 py-1 text-xs font-bold rounded-lg border ${
|
||||
locale === "id"
|
||||
? "bg-accent/10 border-accent/20 text-accent"
|
||||
: "bg-muted/50 border-border/50 text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
ID
|
||||
</button>
|
||||
<button
|
||||
onClick={() => switchLocale("en")}
|
||||
className={`px-3 py-1 text-xs font-bold rounded-lg border ${
|
||||
locale === "en"
|
||||
? "bg-accent/10 border-accent/20 text-accent"
|
||||
: "bg-muted/50 border-border/50 text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
EN
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user