Add Dockerfiles, .dockerignore and brand logo
- apps/api/Dockerfile: multi-stage pnpm build, runtime node:22-alpine, port 3001 - apps/web/Dockerfile: multi-stage pnpm + Vite, runtime nginx:alpine with SPA config - .dockerignore: excludes node_modules, dist, .env, .git, IDE folders, design docs - Brand logo (naturcalabacera.webp) integrated as favicon, sidebar icon, header logo - index.html title updated to 'Naturcalabacera · Reservas'
This commit is contained in:
63
.dockerignore
Normal file
63
.dockerignore
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# Dependencias y builds (se generan dentro del contenedor)
|
||||||
|
node_modules
|
||||||
|
**/node_modules
|
||||||
|
dist
|
||||||
|
**/dist
|
||||||
|
|
||||||
|
# Variables de entorno (NUNCA en imágenes)
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
**/.env
|
||||||
|
**/.env.*
|
||||||
|
!**/.env.example
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Editor / SO
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
logs
|
||||||
|
|
||||||
|
# Documentación y archivos de planificación que no necesita el build
|
||||||
|
*.md
|
||||||
|
!README.md
|
||||||
|
architecture
|
||||||
|
directivas
|
||||||
|
skills
|
||||||
|
.agents
|
||||||
|
.claude
|
||||||
|
.codebuddy
|
||||||
|
.commandcode
|
||||||
|
.continue
|
||||||
|
.crush
|
||||||
|
.factory
|
||||||
|
.goose
|
||||||
|
.kilocode
|
||||||
|
.kiro
|
||||||
|
.mcpjam
|
||||||
|
.mux
|
||||||
|
.neovate
|
||||||
|
.openhands
|
||||||
|
.pi
|
||||||
|
.qoder
|
||||||
|
.qwen
|
||||||
|
.roo
|
||||||
|
.tmp
|
||||||
|
.trae
|
||||||
|
.windsurf
|
||||||
|
.zencoder
|
||||||
|
findings.md
|
||||||
|
gemini.md
|
||||||
|
progress.md
|
||||||
|
task_plan.md
|
||||||
|
|
||||||
|
# Imágenes de diseño en raíz
|
||||||
|
*.png
|
||||||
38
apps/api/Dockerfile
Normal file
38
apps/api/Dockerfile
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
# ───── Stage 1: build ─────
|
||||||
|
FROM node:22-alpine AS builder
|
||||||
|
WORKDIR /repo
|
||||||
|
RUN corepack enable
|
||||||
|
|
||||||
|
# Manifiestos primero (para cachear pnpm install si no cambian deps)
|
||||||
|
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
|
||||||
|
COPY packages/shared/package.json ./packages/shared/
|
||||||
|
COPY apps/api/package.json ./apps/api/
|
||||||
|
|
||||||
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# Código fuente
|
||||||
|
COPY packages/shared ./packages/shared
|
||||||
|
COPY apps/api ./apps/api
|
||||||
|
|
||||||
|
# Compila TypeScript del api
|
||||||
|
RUN pnpm --filter @naturcalabacera/api build
|
||||||
|
|
||||||
|
# Carpeta de despliegue self-contained con node_modules de producción
|
||||||
|
RUN pnpm --filter @naturcalabacera/api deploy --prod /out
|
||||||
|
|
||||||
|
# Copia plantillas HTML al dist (tsc no las copia)
|
||||||
|
RUN cp -r apps/api/src/templates /out/dist/templates
|
||||||
|
|
||||||
|
# ───── Stage 2: runtime ─────
|
||||||
|
FROM node:22-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
COPY --from=builder /out/dist ./dist
|
||||||
|
COPY --from=builder /out/node_modules ./node_modules
|
||||||
|
COPY --from=builder /out/package.json ./package.json
|
||||||
|
|
||||||
|
EXPOSE 3001
|
||||||
|
CMD ["node", "dist/index.js"]
|
||||||
52
apps/web/Dockerfile
Normal file
52
apps/web/Dockerfile
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
# ───── Stage 1: build ─────
|
||||||
|
FROM node:22-alpine AS builder
|
||||||
|
WORKDIR /repo
|
||||||
|
RUN corepack enable
|
||||||
|
|
||||||
|
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
|
||||||
|
COPY packages/shared/package.json ./packages/shared/
|
||||||
|
COPY apps/web/package.json ./apps/web/
|
||||||
|
|
||||||
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
COPY packages/shared ./packages/shared
|
||||||
|
COPY apps/web ./apps/web
|
||||||
|
|
||||||
|
# Variables públicas inyectadas en el bundle por Vite (build-time)
|
||||||
|
ARG VITE_SUPABASE_URL
|
||||||
|
ARG VITE_SUPABASE_ANON_KEY
|
||||||
|
ARG VITE_SUPABASE_SCHEMA
|
||||||
|
ARG VITE_API_URL
|
||||||
|
ENV VITE_SUPABASE_URL=$VITE_SUPABASE_URL
|
||||||
|
ENV VITE_SUPABASE_ANON_KEY=$VITE_SUPABASE_ANON_KEY
|
||||||
|
ENV VITE_SUPABASE_SCHEMA=$VITE_SUPABASE_SCHEMA
|
||||||
|
ENV VITE_API_URL=$VITE_API_URL
|
||||||
|
|
||||||
|
RUN pnpm --filter @naturcalabacera/web build
|
||||||
|
|
||||||
|
# ───── Stage 2: runtime (Nginx sirviendo SPA) ─────
|
||||||
|
FROM nginx:alpine
|
||||||
|
COPY --from=builder /repo/apps/web/dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
# Config SPA: cualquier ruta cae en index.html (React Router friendly)
|
||||||
|
RUN printf 'server {\n\
|
||||||
|
listen 80;\n\
|
||||||
|
server_name _;\n\
|
||||||
|
root /usr/share/nginx/html;\n\
|
||||||
|
index index.html;\n\
|
||||||
|
\n\
|
||||||
|
location / {\n\
|
||||||
|
try_files $uri $uri/ /index.html;\n\
|
||||||
|
}\n\
|
||||||
|
\n\
|
||||||
|
# Cache largo para assets con hash\n\
|
||||||
|
location /assets/ {\n\
|
||||||
|
expires 1y;\n\
|
||||||
|
add_header Cache-Control "public, immutable";\n\
|
||||||
|
}\n\
|
||||||
|
}\n' > /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
@@ -2,9 +2,10 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/webp" href="/naturcalabacera.webp" />
|
||||||
|
<link rel="apple-touch-icon" href="/naturcalabacera.webp" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>reservas-app</title>
|
<title>Naturcalabacera · Reservas</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
BIN
apps/web/public/naturcalabacera.webp
Normal file
BIN
apps/web/public/naturcalabacera.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
@@ -126,13 +126,20 @@ function AppContent() {
|
|||||||
<header className={`${theme.headerBg} backdrop-blur-xl border-b ${theme.headerBorder} px-4 md:px-10 py-4 md:py-6 shadow-2xl transition-all duration-700`}>
|
<header className={`${theme.headerBg} backdrop-blur-xl border-b ${theme.headerBorder} px-4 md:px-10 py-4 md:py-6 shadow-2xl transition-all duration-700`}>
|
||||||
{/* Relative container: center title absolutely, left/right content on sides */}
|
{/* Relative container: center title absolutely, left/right content on sides */}
|
||||||
<div className="relative flex items-center justify-center min-h-[3rem] md:min-h-[3.5rem]">
|
<div className="relative flex items-center justify-center min-h-[3rem] md:min-h-[3.5rem]">
|
||||||
{/* Left: status dot + tagline (desktop only) */}
|
{/* Left: brand logo + status dot + tagline */}
|
||||||
<div className="absolute left-0 hidden md:flex items-center gap-2">
|
<div className="absolute left-0 flex items-center gap-2 md:gap-3">
|
||||||
|
<img
|
||||||
|
src="/naturcalabacera.webp"
|
||||||
|
alt="Naturcalabacera"
|
||||||
|
className="w-9 h-9 md:w-11 md:h-11 object-contain drop-shadow-lg flex-shrink-0"
|
||||||
|
/>
|
||||||
|
<div className="hidden md:flex items-center gap-2">
|
||||||
<span className={`inline-block w-2 h-2 rounded-full shadow-lg ${theme.dotColor} ${theme.dotShadow} animate-pulse flex-shrink-0`} />
|
<span className={`inline-block w-2 h-2 rounded-full shadow-lg ${theme.dotColor} ${theme.dotShadow} animate-pulse flex-shrink-0`} />
|
||||||
<span className={`text-xs font-medium ${theme.subtitleText}`}>
|
<span className={`text-xs font-medium ${theme.subtitleText}`}>
|
||||||
Gestiona tus reservas y disponibilidad
|
Gestiona tus reservas y disponibilidad
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Center: property name — always truly centered */}
|
{/* Center: property name — always truly centered */}
|
||||||
<h1
|
<h1
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function Sidebar({ currentView, onNavigate, isViewer = false, isAdmin = f
|
|||||||
<div className={`p-8 border-b ${theme.dividerBorder}`}>
|
<div className={`p-8 border-b ${theme.dividerBorder}`}>
|
||||||
<h1 className="text-3xl font-black text-white flex items-center gap-3">
|
<h1 className="text-3xl font-black text-white flex items-center gap-3">
|
||||||
<div className={`p-2 bg-gradient-to-br ${theme.logoIconBg} rounded-xl shadow-lg ${theme.logoIconShadow} transition-all duration-700`}>
|
<div className={`p-2 bg-gradient-to-br ${theme.logoIconBg} rounded-xl shadow-lg ${theme.logoIconShadow} transition-all duration-700`}>
|
||||||
<Calendar className="w-6 h-6 text-white" />
|
<img src="/naturcalabacera.webp" alt="Naturcalabacera" className="w-6 h-6 object-contain" />
|
||||||
</div>
|
</div>
|
||||||
<span className={`bg-gradient-to-r ${theme.logoTextGradient} bg-clip-text text-transparent transition-all duration-700`}>
|
<span className={`bg-gradient-to-r ${theme.logoTextGradient} bg-clip-text text-transparent transition-all duration-700`}>
|
||||||
Reservas
|
Reservas
|
||||||
|
|||||||
Reference in New Issue
Block a user