Archived
Compare commits
21
Commits
42768fbdbe
..
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4ce2ce413 | ||
|
|
38cf54c2e8 | ||
|
|
0d853e695f | ||
|
|
6f68f2069d | ||
|
|
2fa30e0ed1 | ||
|
|
5132ab81b7 | ||
|
|
13904c9992 | ||
|
|
eab07b849c | ||
|
|
b3477bb980 | ||
|
|
fb90fb637a | ||
|
|
d3cb564e1e | ||
|
|
16d874cf54 | ||
|
|
71e705f7c1 | ||
|
|
eb51c768c7 | ||
|
|
4bb159c7d5 | ||
|
|
2d494e0bb1 | ||
|
|
6212251792 | ||
|
|
aa27818f35 | ||
|
|
39273d6661 | ||
|
|
ea1176a2fd | ||
|
|
1ffad8a9ba |
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules
|
||||||
|
.pnpm-store
|
||||||
|
.next
|
||||||
|
.env
|
||||||
|
.DS_Store
|
||||||
@@ -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
@@ -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"]
|
||||||
@@ -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;
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user