feat: implement initial fullstack portfolio application including dashboard, CMS, and analytics features.

This commit is contained in:
Moh Dzulfikri Maulana
2026-03-07 16:32:49 +07:00
commit bdd61d11d3
59 changed files with 11107 additions and 0 deletions

38
src/lib/constant.ts Normal file
View File

@@ -0,0 +1,38 @@
export const NavLinks = [
{ label: "About", href: "#about" },
{ label: "Stack", href: "#stack" },
{ label: "Projects", href: "#projects" },
{ label: "Experience", href: "#experience" },
{ label: "Contact", href: "#contact" },
];
export const ContactUrls = {
github: "https://github.com/Dzuuul",
linkedin: "https://www.linkedin.com/in/dzulfikrimaulana",
email: "mailto:developer@dzulfikri.com?subject=Inquiry:%20Fullstack%20Developer%20Opportunity%20-%20[Company/Project%20Name]&body=Hi%20Fikri,%0D%0A%0D%0AI%20am%20[Name]%20from%20[Company/Organization%20Name].%0D%0A%0D%0AI%20recently%20explored%20your%20portfolio%20and%20was%20impressed%20by%20your%20work%20as%20a%20Fullstack%20Developer.%20I%20would%20like%20to%20discuss%20a%20potential%20opportunity%20with%20you,%20whether%20it%20be%20for%20a%20career%20role%20within%20our%20team%20or%20a%20professional%20collaboration%20on%20an%20upcoming%20project.%0D%0A%0D%0AAre%20you%20available%20for%20a%20brief%20introductory%20call%20or%20a%20chat%20sometime%20this%20week%20to%20discuss%20this%20further?%0D%0A%0D%0ABest%20regards,%0D%0A[Name]%0D%0A[LinkedIn%20Profile/Contact%20Info]"
}
export const HeroSection = {
badge: "Available for opportunities",
headline: "Fullstack",
headline2: "Developer",
tagline1: "Specializing in Next.js & NestJS",
tagline2:
"Building scalable web applications with Next.js and NestJS, focused on performance, clean architecture, and real-world systems.",
image: "/images/hero-portrait.png",
};
export const AboutSection = {
title: "About Me",
description: "",
};
export const ArchitectureSection = {
title: "Architecture",
description: "",
};
export const ContactSection = {
title: "Contact Me",
description: "",
};

23
src/lib/prisma.ts Normal file
View File

@@ -0,0 +1,23 @@
import { PrismaClient } from "@prisma/client";
import { Pool } from "pg";
import { PrismaPg } from "@prisma/adapter-pg";
const connectionString = `${process.env.DATABASE_URL}`;
const pool = new Pool({ connectionString });
const adapter = new PrismaPg(pool);
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
adapter,
log:
process.env.NODE_ENV === "development"
? ["query", "error", "warn"]
: ["error"],
});
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;

23
src/lib/s3.ts Normal file
View File

@@ -0,0 +1,23 @@
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const s3 = new S3Client({
region: "auto",
endpoint: process.env.R2_ENDPOINT,
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY!,
secretAccessKey: process.env.R2_SECRET_KEY!,
},
});
export async function uploadFile(file: File) {
const buffer = Buffer.from(await file.arrayBuffer());
await s3.send(
new PutObjectCommand({
Bucket: process.env.R2_BUCKET_NAME,
Key: file.name,
Body: buffer,
ContentType: file.type,
}),
);
}

6
src/lib/utils.ts Normal file
View File

@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

10
src/lib/visitor.ts Normal file
View File

@@ -0,0 +1,10 @@
export function getVisitorId() {
let visitorId = localStorage.getItem("visitor_id");
if (!visitorId) {
visitorId = crypto.randomUUID();
localStorage.setItem("visitor_id", visitorId);
}
return visitorId;
}