From f86b7f39f71b0ce3e85db8a35fc6147be0e84cdd Mon Sep 17 00:00:00 2001 From: MOH Date: Fri, 6 Mar 2026 13:53:57 +0100 Subject: [PATCH] feat: initialize mohfarawati app foundation --- .dockerignore | 7 + .env.example | 7 + .github/workflows/ci.yml | 33 + .gitignore | 1 + Dockerfile | 12 + Makefile | 70 + README.md | 34 + app/[locale]/(site)/about/page.tsx | 122 ++ app/[locale]/(site)/contact/page.tsx | 120 + app/[locale]/(site)/layout.tsx | 18 + app/[locale]/(site)/page.tsx | 206 ++ app/[locale]/(site)/portfolio/[slug]/page.tsx | 131 ++ app/[locale]/(site)/portfolio/page.tsx | 77 + app/[locale]/(site)/products/[slug]/page.tsx | 116 + app/[locale]/(site)/products/page.tsx | 75 + app/[locale]/(site)/root/page.tsx | 324 +++ app/[locale]/(site)/success/page.tsx | 60 + app/[locale]/coming-soon/page.tsx | 116 + app/[locale]/layout.tsx | 44 + app/api/health/route.ts | 35 + app/api/maintenance/route.ts | 25 + app/globals.css | 9 +- app/layout.tsx | 24 +- app/page.tsx | 102 +- components/footer.tsx | 38 + components/motion-fade.tsx | 26 + components/navbar.tsx | 110 + components/theme-provider.tsx | 7 + components/theme-toggle.tsx | 40 + docker-compose.yml | 44 + i18n/request.ts | 17 + i18n/routing.ts | 6 + lib/admin-auth.ts | 172 ++ lib/app-config.ts | 28 + lib/prisma.ts | 32 + lib/site-data.ts | 162 ++ lib/utils.ts | 6 + messages/de.json | 22 + messages/en.json | 22 + middleware.ts | 147 ++ next.config.mjs | 21 +- package-lock.json | 1943 ++++++++++++++++- package.json | 34 +- prisma.config.ts | 13 + .../migration.sql | 13 + prisma/migrations/migration_lock.toml | 3 + prisma/schema.prisma | 15 + prisma/seed.js | 28 + public/logos/dark-primary.svg | 25 + public/logos/light-primary.svg | 25 + 50 files changed, 4615 insertions(+), 152 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .github/workflows/ci.yml create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 app/[locale]/(site)/about/page.tsx create mode 100644 app/[locale]/(site)/contact/page.tsx create mode 100644 app/[locale]/(site)/layout.tsx create mode 100644 app/[locale]/(site)/page.tsx create mode 100644 app/[locale]/(site)/portfolio/[slug]/page.tsx create mode 100644 app/[locale]/(site)/portfolio/page.tsx create mode 100644 app/[locale]/(site)/products/[slug]/page.tsx create mode 100644 app/[locale]/(site)/products/page.tsx create mode 100644 app/[locale]/(site)/root/page.tsx create mode 100644 app/[locale]/(site)/success/page.tsx create mode 100644 app/[locale]/coming-soon/page.tsx create mode 100644 app/[locale]/layout.tsx create mode 100644 app/api/health/route.ts create mode 100644 app/api/maintenance/route.ts create mode 100644 components/footer.tsx create mode 100644 components/motion-fade.tsx create mode 100644 components/navbar.tsx create mode 100644 components/theme-provider.tsx create mode 100644 components/theme-toggle.tsx create mode 100644 docker-compose.yml create mode 100644 i18n/request.ts create mode 100644 i18n/routing.ts create mode 100644 lib/admin-auth.ts create mode 100644 lib/app-config.ts create mode 100644 lib/prisma.ts create mode 100644 lib/site-data.ts create mode 100644 lib/utils.ts create mode 100644 messages/de.json create mode 100644 messages/en.json create mode 100644 middleware.ts create mode 100644 prisma.config.ts create mode 100644 prisma/migrations/20260306090522_init_app_config/migration.sql create mode 100644 prisma/migrations/migration_lock.toml create mode 100644 prisma/schema.prisma create mode 100644 prisma/seed.js create mode 100755 public/logos/dark-primary.svg create mode 100755 public/logos/light-primary.svg 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} +
  • +
+
+
+ + +
+
+ + + + +