13 Commits
Author SHA1 Message Date
pablo a4ce2ce413 added form submission 2025-07-08 18:44:22 +02:00
pablo 38cf54c2e8 removed tech-label highlights; clickable techhnologie links 2025-05-27 19:54:58 +02:00
pablo 0d853e695f updated ci/cd pipeline 2025-03-17 16:02:54 +01:00
pablo 6f68f2069d fixed docker compose traefik network 2025-03-17 15:27:25 +01:00
pablo 2fa30e0ed1 updated docker files 2025-03-17 15:20:40 +01:00
pablo 5132ab81b7 added traefik labels 2025-03-16 16:07:19 +01:00
pablo 13904c9992 CI/CD pipepline 2025-03-16 15:53:28 +01:00
pablo eab07b849c updated CI/CD pipeline 2025-03-16 15:41:37 +01:00
pablo b3477bb980 Merge pull request 'main' (#4) from main into dev
Reviewed-on: #4
2025-03-16 14:38:12 +00:00
pablo fb90fb637a Merge pull request 'added gitea CI/CD pipepline' (#3) from dev into main
Deploy Next.js App / deploy (push) Failing after 0s
Reviewed-on: #3
2025-03-16 14:34:27 +00:00
pablo d3cb564e1e added gitea CI/CD pipepline 2025-03-16 15:32:51 +01:00
pablo 16d874cf54 Merge pull request 'added dockerfiles' (#1) from dev into main
Reviewed-on: #1
2025-03-16 14:26:35 +00:00
pablo 71e705f7c1 added dockerfiles 2025-03-16 15:25:22 +01:00
12 changed files with 113 additions and 53 deletions
+5
View File
@@ -0,0 +1,5 @@
node_modules
.pnpm-store
.next
.env
.DS_Store
+19
View File
@@ -0,0 +1,19 @@
name: Deploy Next.js App
on:
push:
branches:
- main
jobs:
deploy:
runs-on: linux
steps:
- name: Setup SSH Key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H "${{ secrets.SSH_HOST }}" >> ~/.ssh/known_hosts
- name: Deploy via SSH
run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "bash ~/deploy.sh ${{ secrets.PROJECT_DEPLOYMENT_PATH }}"
+15
View File
@@ -0,0 +1,15 @@
FROM node:18-alpine
RUN npm install -g pnpm
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
CMD ["pnpm", "start"]
+2 -35
View File
@@ -1,36 +1,3 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). # Pablo Shortman Portfolio
## Getting Started My personal portfolio site built with Next.js.
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
@@ -2,7 +2,11 @@ import React from "react";
import Contact from "@/components/setions/contact"; import Contact from "@/components/setions/contact";
function ContactPage() { function ContactPage() {
return <Contact />; return (
<div className="pt-12">
<Contact />
</div>
);
} }
export default ContactPage; export default ContactPage;
+13 -6
View File
@@ -24,6 +24,7 @@ import {
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { sendDiscordNotification } from "@/lib/actions";
const formSchema = z.object({ const formSchema = z.object({
name: z.string().min(2).max(50), name: z.string().min(2).max(50),
@@ -39,10 +40,16 @@ function ContactForm() {
}); });
// 2. Define a submit handler. // 2. Define a submit handler.
function onSubmit(values: z.infer<typeof formSchema>) { async function onSubmit({
// Do something with the form values. email,
// ✅ This will be type-safe and validated. message,
console.log(values); name,
budget,
}: z.infer<typeof formSchema>) {
await sendDiscordNotification(
`📬 **New Lead Submission**\n*Source*: pablo.shortman.me \n**Name**: ${name}\n**Email**: ${email}\n**Message**: ${message} \n**Budget**: ${budget} $`
);
form.reset();
} }
return ( return (
@@ -98,8 +105,8 @@ function ContactForm() {
<SelectValue placeholder="Select your Budget" /> <SelectValue placeholder="Select your Budget" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="< €2k">{"less then $2k"}</SelectItem> <SelectItem value="< €2k">{"less then $1k"}</SelectItem>
<SelectItem value="> €4k">{"more then $4k"}</SelectItem> <SelectItem value="> €4k">{"more then $3k"}</SelectItem>
<SelectItem value="> €6k">{"more then $6k"}</SelectItem> <SelectItem value="> €6k">{"more then $6k"}</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
+26 -11
View File
@@ -1,10 +1,16 @@
import Link from "next/link";
import React from "react"; import React from "react";
export type TechIcon = "ts" | "next" | "react" | "tailwind" | "PostgreSQL"; export type TechIcon = "ts" | "next" | "react" | "tailwind" | "PostgreSQL";
const tech: { label: string; Logo: (props: any) => JSX.Element }[] = [ const tech: {
label: string;
link: string;
Logo: (props: any) => JSX.Element;
}[] = [
{ {
label: "Tailwind CSS", label: "Tailwind CSS",
link: "https://tailwindcss.com/",
Logo: (props: any) => ( Logo: (props: any) => (
<svg <svg
{...props} {...props}
@@ -45,6 +51,7 @@ const tech: { label: string; Logo: (props: any) => JSX.Element }[] = [
}, },
{ {
label: "Typescript", label: "Typescript",
link: "https://www.typescriptlang.org/",
Logo: (props: any) => ( Logo: (props: any) => (
<svg <svg
{...props} {...props}
@@ -72,6 +79,7 @@ const tech: { label: string; Logo: (props: any) => JSX.Element }[] = [
}, },
{ {
label: "Next JS", label: "Next JS",
link: "https://nextjs.org/",
Logo: (props: any) => ( Logo: (props: any) => (
<svg <svg
{...props} {...props}
@@ -99,6 +107,8 @@ const tech: { label: string; Logo: (props: any) => JSX.Element }[] = [
}, },
{ {
label: "Drizzle ORM", label: "Drizzle ORM",
link: "https://orm.drizzle.team/",
Logo: (props: any) => ( Logo: (props: any) => (
<svg <svg
{...props} {...props}
@@ -128,6 +138,7 @@ const tech: { label: string; Logo: (props: any) => JSX.Element }[] = [
{ {
label: "PostgreSQL", label: "PostgreSQL",
link: "https://www.postgresql.org/",
Logo: (props: any) => ( Logo: (props: any) => (
<svg <svg
{...props} {...props}
@@ -157,6 +168,7 @@ const tech: { label: string; Logo: (props: any) => JSX.Element }[] = [
{ {
label: "Figma", label: "Figma",
link: "https://www.figma.com",
Logo: (props: any) => ( Logo: (props: any) => (
<svg <svg
{...props} {...props}
@@ -184,6 +196,7 @@ const tech: { label: string; Logo: (props: any) => JSX.Element }[] = [
}, },
{ {
label: "Github", label: "Github",
link: "https://github.com/",
Logo: (props: any) => ( Logo: (props: any) => (
<svg <svg
{...props} {...props}
@@ -215,18 +228,20 @@ function Tech() {
return ( return (
<div className="py-12 lg:py-20 container relative space-y-8"> <div className="py-12 lg:py-20 container relative space-y-8">
<h3 className="text-sm font-mono text-muted-foreground text-center"> <h3 className="text-sm font-mono text-muted-foreground text-center">
most liked{" "} most liked <span className="text-foreground">technologies</span> and{" "}
<span className="underline text-foreground">technologies</span> and{" "} <span className="text-foreground">tools</span>
<span className="underline text-foreground">tools</span>
</h3> </h3>
<ul className="flex w-full items-center justify-center gap-2 md:gap-4 lg:gap-8 flex-wrap"> <ul className="flex w-full items-center justify-center gap-2 md:gap-4 lg:gap-8 flex-wrap">
{tech.map(({ Logo, label }, idx) => ( {tech.map(({ Logo, label, link }, idx) => (
<li <li key={idx}>
className="flex items-center gap-2 py-2 px-6 rounded-md bg-muted" <Link
key={idx} href={link}
> target="_blank"
<Logo className="size-6" /> className="flex items-center gap-2 py-2 px-6 rounded-md bg-muted"
<span>{label}</span> >
<Logo className="size-6" />
<span>{label}</span>
</Link>
</li> </li>
))} ))}
</ul> </ul>
+13
View File
@@ -0,0 +1,13 @@
"use server";
export async function sendDiscordNotification(message: string) {
const webhookUrl = process.env.DISCORD_WEBHOOK_URL;
await fetch(webhookUrl!, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
content: message,
}),
});
}
+15
View File
@@ -0,0 +1,15 @@
labels:
- "traefik.enable=true"
- "traefik.http.routers.logipedia.rule=Host(`logipedia.shortman.me`)"
- "traefik.http.routers.logipedia.entrypoints=websecure"
- "traefik.http.routers.logipedia.tls.certresolver=myresolver"
networks:
- logipedia_network
- webproxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.pablo-shortman-me.rule=Host(`pablo.shortman.me`)"
- "traefik.http.routers.pablo-shortman-me.entrypoints=websecure"
- "traefik.http.routers.pablo-shortman-me.tls.certresolver=myresolver"
networks:
- webproxy