From 6f7624c3910a9028f7db100040c5f215aae19134 Mon Sep 17 00:00:00 2001 From: anggapixa Date: Tue, 16 Dec 2025 03:07:19 +0700 Subject: [PATCH 1/8] feat: implement all basic public page --- .env.example | 4 + CLAUDE.md | 11 + next.config.ts | 15 + package-lock.json | 19 +- package.json | 1 + src/app/about/page.tsx | 276 ++++++++++++++++++ src/app/blog/loading.tsx | 50 ++++ src/app/blog/page.tsx | 125 ++++++++ src/app/careers/page.tsx | 351 +++++++++++++++++++++++ src/app/contact/page.tsx | 341 ++++++++++++++++++++++ src/app/features/page.tsx | 309 ++++++++++++++++++++ src/app/pricing/page.tsx | 303 ++++++++++++++++++++ src/app/privacy/page.tsx | 250 ++++++++++++++++ src/app/security/page.tsx | 381 +++++++++++++++++++++++++ src/app/showcase/page.tsx | 355 +++++++++++++++++++++++ src/app/terms/page.tsx | 307 ++++++++++++++++++++ src/components/blog/BlogGrid.tsx | 106 +++++++ src/components/blog/BlogPagination.tsx | 141 +++++++++ src/components/blog/README.md | 174 +++++++++++ src/components/blog/index.ts | 2 + 20 files changed, 3503 insertions(+), 18 deletions(-) create mode 100644 src/app/about/page.tsx create mode 100644 src/app/blog/loading.tsx create mode 100644 src/app/blog/page.tsx create mode 100644 src/app/careers/page.tsx create mode 100644 src/app/contact/page.tsx create mode 100644 src/app/features/page.tsx create mode 100644 src/app/pricing/page.tsx create mode 100644 src/app/privacy/page.tsx create mode 100644 src/app/security/page.tsx create mode 100644 src/app/showcase/page.tsx create mode 100644 src/app/terms/page.tsx create mode 100644 src/components/blog/BlogGrid.tsx create mode 100644 src/components/blog/BlogPagination.tsx create mode 100644 src/components/blog/README.md create mode 100644 src/components/blog/index.ts diff --git a/.env.example b/.env.example index 1f98b6c..33f62eb 100644 --- a/.env.example +++ b/.env.example @@ -35,3 +35,7 @@ BASESCAN_API_KEY="your_basescan_api_key" # Mapbox (for scan location maps) NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN="your_mapbox_access_token" + +# Ghost Blog API (for blog posts) +NEXT_PUBLIC_GHOST_URL="https://blog.example.com" +NEXT_PUBLIC_TOKEN="your_ghost_content_api_key" diff --git a/CLAUDE.md b/CLAUDE.md index 2f90b32..fac8874 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -106,6 +106,16 @@ Server actions are organized in `src/lib/actions/`: **Public Routes:** - `/` - Public landing page +- `/about` - About Etags, mission, vision, team +- `/features` - Comprehensive features showcase +- `/pricing` - Pricing plans and FAQs +- `/showcase` - Success stories and testimonials +- `/careers` - Job openings and company culture +- `/contact` - Contact form and information +- `/blog` - Blog articles from Ghost CMS +- `/privacy` - Privacy policy +- `/terms` - Terms and conditions +- `/security` - Security practices and policies - `/login` - Login page (redirects to /manage if authenticated) - `/register` - User registration page - `/scan` - QR code scanner for tag verification @@ -249,3 +259,4 @@ Copy `.env.example` to `.env` and configure: - `KOLOSAL_API_KEY` - Kolosal AI for fraud detection - `BASESCAN_API_KEY` - BaseScan API for explorer features - `NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN` - Mapbox token for scan location maps +- `NEXT_PUBLIC_TOKEN` - Ghost CMS Content API key for blog posts diff --git a/next.config.ts b/next.config.ts index 7f742f4..c3d17b6 100644 --- a/next.config.ts +++ b/next.config.ts @@ -23,6 +23,21 @@ const nextConfig: NextConfig = { protocol: 'https', hostname: '*.cloudflarestorage.com', }, + { + // Ghost CMS blog images + protocol: 'https', + hostname: 'blog.javapixa.com', + }, + { + // Unsplash images (used by Ghost) + protocol: 'https', + hostname: 'images.unsplash.com', + }, + { + // Ghost CDN + protocol: 'https', + hostname: 'static.ghost.org', + }, ], }, }; diff --git a/package-lock.json b/package-lock.json index f87b5d2..5baee4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,6 @@ "@aws-sdk/s3-request-presigner": "^3.705.0", "@fingerprintjs/fingerprintjs": "^5.0.1", "@google/genai": "^1.30.0", - "@gsap/react": "^2.1.2", "@prisma/client": "^6.1.0", "@radix-ui/react-avatar": "^1.1.11", "@radix-ui/react-checkbox": "^1.3.3", @@ -24,12 +23,12 @@ "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", "@types/mapbox-gl": "^3.4.1", + "axios": "^1.13.2", "bcryptjs": "^3.0.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "ethers": "^6.13.4", "framer-motion": "^12.23.25", - "gsap": "^3.13.0", "html5-qrcode": "^2.3.8", "lucide-react": "^0.555.0", "mapbox-gl": "^3.16.0", @@ -2454,16 +2453,6 @@ } } }, - "node_modules/@gsap/react": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@gsap/react/-/react-2.1.2.tgz", - "integrity": "sha512-JqliybO1837UcgH2hVOM4VO+38APk3ECNrsuSM4MuXp+rbf+/2IG2K1YJiqfTcXQHH7XlA0m3ykniFYstfq0Iw==", - "license": "SEE LICENSE AT https://gsap.com/standard-license", - "peerDependencies": { - "gsap": "^3.12.5", - "react": ">=17" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -11659,12 +11648,6 @@ "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==", "license": "ISC" }, - "node_modules/gsap": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.13.0.tgz", - "integrity": "sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw==", - "license": "Standard 'no charge' license: https://gsap.com/standard-license." - }, "node_modules/gtoken": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", diff --git a/package.json b/package.json index aa0d39c..c005694 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", "@types/mapbox-gl": "^3.4.1", + "axios": "^1.13.2", "bcryptjs": "^3.0.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx new file mode 100644 index 0000000..ce2e588 --- /dev/null +++ b/src/app/about/page.tsx @@ -0,0 +1,276 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { Navbar } from '@/components/landing/Navbar'; +import { Footer } from '@/components/landing/Footer'; +import { Shield, Target, Users, Zap } from 'lucide-react'; +import Image from 'next/image'; + +const MotionDiv = motion.div; +const MotionH1 = motion.h1; +const MotionP = motion.p; + +export default function AboutPage() { + const values = [ + { + icon: Shield, + title: 'Keamanan', + description: + 'Teknologi blockchain yang tidak dapat dipalsukan untuk melindungi keaslian produk.', + }, + { + icon: Target, + title: 'Inovasi', + description: + 'Terus mengembangkan solusi terdepan untuk tantangan autentikasi produk.', + }, + { + icon: Users, + title: 'Kolaborasi', + description: + 'Bekerja sama dengan brand dan mitra untuk menciptakan ekosistem yang aman.', + }, + { + icon: Zap, + title: 'Efisiensi', + description: + 'Proses verifikasi cepat dan mudah untuk pengalaman pengguna yang optimal.', + }, + ]; + + const team = [ + { + name: 'igun997', + role: 'Lead Developer', + github: 'https://github.com/igun997', + }, + { + name: 'inact25', + role: 'Blockchain Engineer', + github: 'https://github.com/inact25', + }, + { + name: 'juanaf31', + role: 'Full Stack Developer', + github: 'https://github.com/juanaf31', + }, + { + name: 'ramaramx', + role: 'AI/ML Engineer', + github: 'https://github.com/ramaramx', + }, + ]; + + return ( +
+ {/* Background Effects */} +
+
+
+
+ + + + {/* Main Content */} +
+
+ {/* Hero Section */} +
+ + Tentang Etags + + + Platform verifikasi produk berbasis blockchain yang mengamankan + rantai pasokan dan memberikan kepercayaan penuh kepada konsumen. + +
+ + {/* Mission & Vision */} +
+ +

+ Misi Kami +

+

+ Memberikan solusi autentikasi produk yang aman, transparan, dan + mudah digunakan untuk melindungi konsumen dari produk palsu dan + meningkatkan kepercayaan terhadap brand. +

+
+ + +

+ Visi Kami +

+

+ Menjadi standar global untuk verifikasi keaslian produk dengan + teknologi blockchain, menciptakan ekosistem di mana setiap + produk dapat diverifikasi dengan mudah dan aman. +

+
+
+ + {/* Values */} +
+

+ Nilai-Nilai Kami +

+
+ {values.map((value, index) => ( + + +

+ {value.title} +

+

+ {value.description} +

+
+ ))} +
+
+ + {/* Technology Stack */} + +

+ Teknologi Kami +

+

+ Etags dibangun dengan teknologi terdepan untuk memberikan + keamanan, kecepatan, dan skalabilitas yang optimal. +

+
+
+

Blockchain

+

+ Base Sepolia (Layer 2 Ethereum) +

+
+
+

+ Smart Contracts +

+

+ ERC721 NFT & ETagRegistry +

+
+
+

AI/ML

+

Kolosal AI & Gemini AI

+
+
+

Framework

+

Next.js 16 & React 19

+
+
+

Database

+

MySQL & Prisma ORM

+
+
+

Storage

+

Cloudflare R2

+
+
+
+ + {/* Team */} +
+

Tim Kami

+

+ Tim Pemuja Deadline Anti Refund - Developer berpengalaman yang + berdedikasi membangun solusi blockchain terbaik untuk IMPHEN 2025. +

+
+ {team.map((member, index) => ( + +
+ +
+

+ {member.name} +

+

{member.role}

+ + GitHub Profile + +
+ ))} +
+
+ + {/* Stats */} +
+ {[ + { value: '99.9%', label: 'Uptime Sistem' }, + { value: '<1dtk', label: 'Waktu Verifikasi' }, + { value: 'Gas-Free', label: 'NFT Minting' }, + ].map((stat, index) => ( + +
+ {stat.value} +
+
{stat.label}
+
+ ))} +
+
+
+ +
+
+ ); +} diff --git a/src/app/blog/loading.tsx b/src/app/blog/loading.tsx new file mode 100644 index 0000000..1b7750d --- /dev/null +++ b/src/app/blog/loading.tsx @@ -0,0 +1,50 @@ +import { Navbar } from '@/components/landing/Navbar'; +import { Footer } from '@/components/landing/Footer'; + +export default function BlogLoading() { + return ( +
+ {/* Background Effects */} +
+
+
+
+ + + +
+
+ {/* Hero Section Skeleton */} +
+
+
+
+ + {/* Blog Grid Skeleton */} +
+ {[1, 2, 3, 4, 5, 6].map((i) => ( +
+ {/* Image Skeleton */} +
+ + {/* Content Skeleton */} +
+
+
+
+
+
+
+
+ ))} +
+
+
+ +
+
+ ); +} diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx new file mode 100644 index 0000000..daad517 --- /dev/null +++ b/src/app/blog/page.tsx @@ -0,0 +1,125 @@ +import { Metadata } from 'next'; +import { Navbar } from '@/components/landing/Navbar'; +import { Footer } from '@/components/landing/Footer'; +import { BlogGrid } from '@/components/blog/BlogGrid'; +import { BlogPagination } from '@/components/blog/BlogPagination'; +import axios from 'axios'; + +export const metadata: Metadata = { + title: 'Blog - Etags', + description: + 'Insights, berita terbaru, dan artikel mendalam tentang blockchain, product authentication, dan teknologi Web3.', +}; + +interface GhostPagination { + page: number; + limit: number; + pages: number; + total: number; + next: number | null; + prev: number | null; +} + +interface GhostResponse { + posts: Array<{ + title: string; + excerpt: string; + url: string; + feature_image: string; + published_at: string; + }>; + meta: { + pagination: GhostPagination; + }; +} + +const POSTS_PER_PAGE = 12; + +const getPostData = async (page: number = 1): Promise => { + const token = process.env.NEXT_PUBLIC_TOKEN; + const url = process.env.NEXT_PUBLIC_GHOST_URL; + if (!token) { + throw new Error('NEXT_PUBLIC_TOKEN is not set'); + } + const result = await axios.get( + `https://${url}/ghost/api/content/posts/?key=${token}&limit=${POSTS_PER_PAGE}&page=${page}&fields=title,excerpt,url,feature_image,published_at&include=tags,authors` + ); + return result.data; +}; + +export default async function BlogPage({ + searchParams, +}: { + searchParams: { page?: string }; +}) { + const currentPage = Number(searchParams.page) || 1; + let posts: Array<{ + title: string; + excerpt: string; + url: string; + feature_image: string; + published_at: string; + }> = []; + let pagination: GhostPagination | null = null; + let error = null; + + try { + const data = await getPostData(currentPage); + posts = data.posts || []; + pagination = data.meta?.pagination || null; + } catch (e) { + console.error('Failed to fetch blog posts:', e); + error = 'Gagal memuat artikel blog. Silakan coba lagi nanti.'; + } + + return ( +
+ {/* Background Effects */} +
+
+
+
+ + + + {/* Main Content */} +
+
+ {/* Hero Section */} +
+

+ Blog Etags +

+

+ Insights, berita terbaru, dan artikel mendalam tentang blockchain, + product authentication, dan teknologi Web3. +

+
+ + {/* Blog Grid */} + {error ? ( +
+

{error}

+
+ ) : ( + <> + + + {/* Pagination */} + {pagination && pagination.pages > 1 && ( + + )} + + )} +
+
+ +
+
+ ); +} diff --git a/src/app/careers/page.tsx b/src/app/careers/page.tsx new file mode 100644 index 0000000..5fa34fc --- /dev/null +++ b/src/app/careers/page.tsx @@ -0,0 +1,351 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { Navbar } from '@/components/landing/Navbar'; +import { Footer } from '@/components/landing/Footer'; +import { + Briefcase, + Code, + Palette, + Shield, + TrendingUp, + Users, + Zap, + Heart, +} from 'lucide-react'; +import { Button } from '@/components/ui/button'; + +const MotionDiv = motion.div; +const MotionH1 = motion.h1; +const MotionP = motion.p; + +export default function CareersPage() { + const positions = [ + { + title: 'Senior Blockchain Engineer', + department: 'Engineering', + type: 'Full-time', + location: 'Remote', + description: + 'Develop and maintain smart contracts on Base Sepolia, optimize gas usage, and implement new blockchain features.', + requirements: [ + '5+ years experience with Solidity', + 'Deep understanding of EVM and L2 solutions', + 'Experience with ethers.js and Hardhat', + 'Knowledge of security best practices', + ], + }, + { + title: 'Full Stack Developer', + department: 'Engineering', + type: 'Full-time', + location: 'Remote', + description: + 'Build and maintain Next.js application, implement new features, and optimize performance.', + requirements: [ + '3+ years experience with React and Next.js', + 'Strong TypeScript skills', + 'Experience with Prisma ORM and MySQL', + 'Understanding of Web3 integration', + ], + }, + { + title: 'AI/ML Engineer', + department: 'Engineering', + type: 'Full-time', + location: 'Remote', + description: + 'Develop and improve AI fraud detection models, implement NFT art generation, and optimize AI performance.', + requirements: [ + 'Experience with machine learning frameworks', + 'Knowledge of fraud detection patterns', + 'Familiarity with LLMs and image generation', + 'Python and/or TypeScript proficiency', + ], + }, + { + title: 'Product Designer', + department: 'Design', + type: 'Full-time', + location: 'Remote', + description: + 'Design intuitive user interfaces, create design systems, and improve user experience across all touchpoints.', + requirements: [ + 'Strong portfolio of web applications', + 'Experience with Figma and design systems', + 'Understanding of blockchain UX patterns', + 'Ability to conduct user research', + ], + }, + { + title: 'DevOps Engineer', + department: 'Engineering', + type: 'Full-time', + location: 'Remote', + description: + 'Manage infrastructure, implement CI/CD pipelines, monitor system performance, and ensure 99.9% uptime.', + requirements: [ + 'Experience with Docker and Kubernetes', + 'Knowledge of AWS or similar cloud platforms', + 'Familiarity with monitoring tools', + 'Understanding of security best practices', + ], + }, + { + title: 'Customer Success Manager', + department: 'Customer Success', + type: 'Full-time', + location: 'Jakarta/Remote', + description: + 'Help brands succeed with Etags, provide technical support, conduct onboarding, and gather feedback.', + requirements: [ + 'Excellent communication skills', + 'Technical background preferred', + 'Experience with B2B SaaS', + 'Fluent in English and Bahasa Indonesia', + ], + }, + ]; + + const benefits = [ + { + icon: Zap, + title: 'Competitive Salary', + description: + 'Gaji kompetitif dengan equity options untuk early team members', + }, + { + icon: Heart, + title: 'Health Insurance', + description: 'Asuransi kesehatan comprehensive untuk kamu dan keluarga', + }, + { + icon: Users, + title: 'Remote First', + description: 'Kerja dari mana saja dengan flexible working hours', + }, + { + icon: TrendingUp, + title: 'Career Growth', + description: 'Learning budget dan mentorship untuk pengembangan karir', + }, + { + icon: Code, + title: 'Latest Tech', + description: 'Bekerja dengan teknologi terdepan: blockchain, AI, Web3', + }, + { + icon: Shield, + title: 'Work-Life Balance', + description: 'Unlimited PTO dan flexible schedule', + }, + ]; + + const values = [ + { + icon: Shield, + title: 'Integrity', + description: + 'Kami berkomitmen untuk transparansi dan kejujuran dalam setiap aspek.', + }, + { + icon: Zap, + title: 'Innovation', + description: 'Kami terus berinovasi dan mengeksplorasi solusi baru.', + }, + { + icon: Users, + title: 'Collaboration', + description: + 'Kami percaya pada kekuatan teamwork dan komunikasi terbuka.', + }, + { + icon: TrendingUp, + title: 'Excellence', + description: + 'Kami berusaha untuk memberikan hasil terbaik dalam setiap pekerjaan.', + }, + ]; + + return ( +
+ {/* Background Effects */} +
+
+
+
+ + + + {/* Main Content */} +
+
+ {/* Hero Section */} +
+ + Bergabung dengan Tim Etags + + + Bangun masa depan verifikasi produk bersama tim yang passionate + tentang blockchain, AI, dan Web3. Mari ciptakan dampak nyata untuk + brand dan konsumen. + +
+ + {/* Values */} +
+

+ Nilai-Nilai Kami +

+
+ {values.map((value, index) => ( + + +

+ {value.title} +

+

+ {value.description} +

+
+ ))} +
+
+ + {/* Benefits */} + +

+ Benefits & Perks +

+
+ {benefits.map((benefit, index) => ( +
+ +

+ {benefit.title} +

+

+ {benefit.description} +

+
+ ))} +
+
+ + {/* Open Positions */} +
+

+ Posisi yang Tersedia +

+
+ {positions.map((position, index) => ( + +
+
+
+ +

+ {position.title} +

+
+
+ + {position.department} + + + {position.type} + + + {position.location} + +
+

+ {position.description} +

+
+

+ Requirements: +

+
    + {position.requirements.map((req, i) => ( +
  • {req}
  • + ))} +
+
+
+ +
+
+ ))} +
+
+ + {/* CTA Section */} + +

+ Tidak Menemukan Posisi yang Cocok? +

+

+ Kami selalu mencari talenta hebat untuk bergabung dengan tim. + Kirimkan CV dan portfolio kamu, kami akan menghubungi jika ada + posisi yang sesuai. +

+ +
+
+
+ +
+
+ ); +} diff --git a/src/app/contact/page.tsx b/src/app/contact/page.tsx new file mode 100644 index 0000000..b38c2cb --- /dev/null +++ b/src/app/contact/page.tsx @@ -0,0 +1,341 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { Navbar } from '@/components/landing/Navbar'; +import { Footer } from '@/components/landing/Footer'; +import { Mail, MapPin, Phone, Send, MessageSquare, Clock } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Textarea } from '@/components/ui/textarea'; +import { useState } from 'react'; + +const MotionDiv = motion.div; +const MotionH1 = motion.h1; +const MotionP = motion.p; + +export default function ContactPage() { + const [formData, setFormData] = useState({ + name: '', + email: '', + company: '', + subject: '', + message: '', + }); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + console.log('Form submitted:', formData); + }; + + const contactInfo = [ + { + icon: Mail, + title: 'Email', + value: 'hello@etags.id', + link: 'mailto:hello@etags.id', + }, + { + icon: Phone, + title: 'Phone', + value: '+62 21 1234 5678', + link: 'tel:+622112345678', + }, + { + icon: MapPin, + title: 'Address', + value: 'Jakarta, Indonesia', + link: null, + }, + ]; + + const reasons = [ + { + icon: MessageSquare, + title: 'Sales & Demo', + description: + 'Ingin mencoba Etags atau mendiskusikan kebutuhan bisnis Anda?', + email: 'sales@etags.id', + }, + { + icon: Mail, + title: 'Support', + description: + 'Butuh bantuan teknis atau memiliki pertanyaan tentang produk?', + email: 'support@etags.id', + }, + { + icon: Clock, + title: 'Partnership', + description: + 'Tertarik untuk bermitra atau integrasi dengan platform kami?', + email: 'partnership@etags.id', + }, + ]; + + return ( +
+ {/* Background Effects */} +
+
+
+
+ + + + {/* Main Content */} +
+
+ {/* Hero Section */} +
+ + Mari Berbicara + + + Punya pertanyaan, ingin demo, atau tertarik bermitra? Tim kami + siap membantu Anda. + +
+ + {/* Contact Reasons */} +
+ {reasons.map((reason, index) => ( + + +

+ {reason.title} +

+

+ {reason.description} +

+ + {reason.email} + +
+ ))} +
+ + {/* Contact Form & Info */} +
+ {/* Contact Form */} + +

+ Kirim Pesan +

+
+
+
+ + + setFormData({ ...formData, name: e.target.value }) + } + required + className="border-[#A8A8A8]/30 focus:border-[#2B4C7E]" + /> +
+
+ + + setFormData({ ...formData, email: e.target.value }) + } + required + className="border-[#A8A8A8]/30 focus:border-[#2B4C7E]" + /> +
+
+
+ + + setFormData({ ...formData, company: e.target.value }) + } + className="border-[#A8A8A8]/30 focus:border-[#2B4C7E]" + /> +
+
+ + + setFormData({ ...formData, subject: e.target.value }) + } + required + className="border-[#A8A8A8]/30 focus:border-[#2B4C7E]" + /> +
+
+ +