diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..139a534 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +node_modules +.next +.git +.gitignore +npm-debug.log +Dockerfile +.dockerignore diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..44dcb4e --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +DATABASE_URL="postgresql://postgres:postgres@localhost:5432/moh_sass?schema=public" +NEXT_PUBLIC_APP_URL="http://localhost:3000" +NEXT_TELEMETRY_DISABLED="1" +ADMIN_PASSWORD="123Yolo!321" +ADMIN_AUTH_SECRET="Us0z76jwlTQLOeQWAGGxAxDcc0rHwp4q" +ROOT_BASIC_AUTH_USER="root" +ROOT_BASIC_AUTH_PASS="123Yolo!321" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3a6b02b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,33 @@ +name: CI + +on: + push: + pull_request: + +jobs: + quality: + runs-on: ubuntu-latest + env: + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/moh_sass?schema=public + NEXT_TELEMETRY_DISABLED: "1" + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Generate Prisma client + run: npm run prisma:generate + + - name: Lint + run: npm run lint + + - name: Build + run: npm run build diff --git a/.gitignore b/.gitignore index fd3dbb5..a84107a 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ yarn-debug.log* yarn-error.log* # local env files +.env .env*.local # vercel diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a1338fb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM node:22-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm install + +COPY . . + +EXPOSE 3000 + +CMD ["npm", "run", "dev", "--", "--hostname", "0.0.0.0", "--port", "3000"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0a6d610 --- /dev/null +++ b/Makefile @@ -0,0 +1,70 @@ +.PHONY: start stop restart logs build ps port health clean-orphans app-shell db-shell db-init db-migrate db-seed prisma-generate prisma-migrate help + +MIGRATION_NAME ?= init + +start: + docker compose up -d --build + +stop: + docker compose down + +restart: stop start + +logs: + docker compose logs -f --tail=200 + +build: + docker compose build + +ps: + docker compose ps + +port: + docker compose port app 3000 + +clean-orphans: + docker compose up -d --remove-orphans + +app-shell: + docker compose exec app sh + +db-shell: + docker compose exec db psql -U postgres -d moh_sass + +db-init: + docker compose exec app sh -lc "npx prisma generate && npx prisma migrate deploy && npx prisma db seed" + +db-migrate: + docker compose exec app npx prisma migrate deploy + +db-seed: + docker compose exec app npx prisma db seed + +prisma-generate: + docker compose exec app npx prisma generate + +prisma-migrate: + docker compose exec app npx prisma migrate dev --name $(MIGRATION_NAME) + +health: + @PORT=$$(docker compose port app 3000 | sed 's/.*://'); \ + curl -sS "http://localhost:$$PORT/api/health" + +help: + @echo "Available targets:" + @echo " make start Start all containers" + @echo " make stop Stop and remove containers" + @echo " make restart Restart all containers" + @echo " make logs Follow container logs" + @echo " make build Build images" + @echo " make ps Show container status" + @echo " make port Show random host port for app" + @echo " make clean-orphans Remove orphaned old containers" + @echo " make app-shell Open shell in app container" + @echo " make db-shell Open PostgreSQL shell" + @echo " make db-init Generate client, apply migrations, run seed" + @echo " make db-migrate Apply prisma migrations" + @echo " make db-seed Seed database data" + @echo " make prisma-generate Run prisma generate" + @echo " make prisma-migrate Create/apply dev migration" + @echo " make health Check app health endpoint" diff --git a/README.md b/README.md index e215bc4..3660b7e 100644 --- a/README.md +++ b/README.md @@ -34,3 +34,37 @@ You can check out [the Next.js GitHub repository](https://github.com/vercel/next 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. + +## Infrastructure Basics + +1. Copy environment variables: + +```bash +cp .env.example .env +``` + +2. Start containers: + +```bash +make start +make port +``` + +3. Initialize database: + +```bash +make db-init +``` + +4. Run quality checks: + +```bash +npm run lint +npm run build +``` + +5. Health check: + +```bash +make health +``` diff --git a/app/[locale]/(site)/about/page.tsx b/app/[locale]/(site)/about/page.tsx new file mode 100644 index 0000000..8866996 --- /dev/null +++ b/app/[locale]/(site)/about/page.tsx @@ -0,0 +1,122 @@ +import { Compass, Layers3, Users } from "lucide-react"; + +import { MotionFade } from "@/components/motion-fade"; +import { resolveLocale } from "@/lib/site-data"; + +type AboutPageProps = { + params: { + locale: string; + }; +}; + +export default function AboutPage({ params: { locale } }: AboutPageProps) { + const localeKey = resolveLocale(locale); + + const copy = + localeKey === "de" + ? { + title: "Ueber uns", + intro: + "Wir bauen klare digitale Erlebnisse fuer Marken, Produkte und Teams.", + valuesTitle: "Wie wir arbeiten", + valueA: "Strategie zuerst", + valueAText: "Jedes Projekt startet mit Zielbild, Scope und Prioritaeten.", + valueB: "Saubere Systeme", + valueBText: "Wir setzen auf wartbare Komponenten und klare Strukturen.", + valueC: "Enge Zusammenarbeit", + valueCText: "Kurze Schleifen mit direktem Feedback im gesamten Ablauf.", + processTitle: "Unser Ablauf", + processOne: "Discovery und Zieldefinition", + processTwo: "Design und Prototyping", + processThree: "Build, Test und Launch", + } + : { + title: "About", + intro: + "We build clear digital experiences for brands, products and teams.", + valuesTitle: "How we work", + valueA: "Strategy first", + valueAText: "Each project starts with goals, scope and priorities.", + valueB: "Clean systems", + valueBText: "We rely on maintainable components and clear structure.", + valueC: "Close collaboration", + valueCText: "Short loops with direct feedback across the full process.", + processTitle: "Our process", + processOne: "Discovery and goal definition", + processTwo: "Design and prototyping", + processThree: "Build, test and launch", + }; + + return ( +
+ +
+

+ {copy.title} +

+

+ {copy.intro} +

+
+
+ + +
+

+ {copy.valuesTitle} +

+
+
+ +

+ {copy.valueA} +

+

+ {copy.valueAText} +

+
+
+ +

+ {copy.valueB} +

+

+ {copy.valueBText} +

+
+
+ +

+ {copy.valueC} +

+

+ {copy.valueCText} +

+
+
+
+
+ + +
+

+ {copy.processTitle} +

+
    + {[copy.processOne, copy.processTwo, copy.processThree].map((step, index) => ( +
  1. + + {index + 1} + +

    {step}

    +
  2. + ))} +
+
+
+
+ ); +} diff --git a/app/[locale]/(site)/contact/page.tsx b/app/[locale]/(site)/contact/page.tsx new file mode 100644 index 0000000..dbe8f52 --- /dev/null +++ b/app/[locale]/(site)/contact/page.tsx @@ -0,0 +1,120 @@ +import { Mail, MapPin, Phone } from "lucide-react"; +import Link from "next/link"; + +import { MotionFade } from "@/components/motion-fade"; +import { resolveLocale } from "@/lib/site-data"; + +type ContactPageProps = { + params: { + locale: string; + }; +}; + +export default function ContactPage({ params: { locale } }: ContactPageProps) { + const localeKey = resolveLocale(locale); + + const copy = + localeKey === "de" + ? { + title: "Kontakt", + intro: "Schreib uns kurz dein Ziel und wir melden uns zeitnah.", + name: "Name", + email: "E-Mail", + message: "Nachricht", + submit: "Senden", + preview: "Success Seite ansehen", + phone: "+49 30 123456", + mail: "hello@moh-sass.dev", + city: "Berlin, Germany", + } + : { + title: "Contact", + intro: "Share your goal and we will get back quickly.", + name: "Name", + email: "Email", + message: "Message", + submit: "Submit", + preview: "Open success page", + phone: "+49 30 123456", + mail: "hello@moh-sass.dev", + city: "Berlin, Germany", + }; + + return ( +
+ +
+

+ {copy.title} +

+

+ {copy.intro} +

+ +
    +
  • + + {copy.phone} +
  • +
  • + + {copy.mail} +
  • +
  • + + {copy.city} +
  • +
+
+
+ + +
+
+ + + + +