Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ body:
attributes:
label: Шаги воспроизведения
placeholder: |
1. Запустить Server и maze
1. Запустить Server и Client
2. Открыть каталог
3. Добавить товар в корзину
4. Обновить страницу
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/task.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ body:
attributes:
label: Область
options:
- Фронт (maze)
- Фронт (Client)
- Бэкенд (Server)
- Инфра / DevOps
- Документация
Expand Down
16 changes: 16 additions & 0 deletions Client/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
node_modules
.next
.git
.gitignore
Dockerfile
.dockerignore
*.md
.env
.env.*
!.env.example
npm-debug.log*
coverage
.DS_Store
*.tsbuildinfo
next-env.d.ts
.vercel
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions Client/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fund=false
File renamed without changes.
File renamed without changes.
36 changes: 36 additions & 0 deletions Client/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Next.js App Router (SSR) — не статический /dist под nginx.
# Сборка: docker compose build client
FROM node:22-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

FROM node:22-alpine AS build
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Rewrites /api и /uploads в next.config.ts читаются на этапе build.
ARG API_INTERNAL_URL=http://api:4000
ENV API_INTERNAL_URL=$API_INTERNAL_URL
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build

FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV HOSTNAME=0.0.0.0
ENV PORT=3000

RUN addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 nextjs

COPY --from=build /app/public ./public
COPY --from=build --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=build --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]
23 changes: 23 additions & 0 deletions Client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# MAZE Client

Storefront и админка магазина MAZE на Next.js (App Router).

## Запуск

Из корня репозитория подними API (`Server/`, порт 4000), затем:

```bash
cd Client
npm install
npm run dev
```

Открой [http://localhost:3000](http://localhost:3000).

Переменные окружения — см. `.env.example` (URL API, публичный origin).

## Скрипты

- `npm run dev` — development
- `npm run build` / `npm start` — production
- `npm run lint` — ESLint
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function StaffLayout({
return (
<div
data-staff-zone
className="staff-zone flex min-h-dvh flex-1 flex-col bg-[#07080f] text-ink"
className="staff-zone flex min-h-dvh flex-1 flex-col bg-[#080c0d] text-ink"
>
{children}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default async function CatalogPage({
</div>

<CatalogClient
key={`${sp.q ?? ""}:${sp.cat ?? ""}`}
products={products}
initialQuery={sp.q ?? ""}
initialCat={sp.cat}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Suspense } from "react";
import { Background } from "@/components/Background";
import { Header } from "@/components/Header";
import { Footer } from "@/components/Footer";
import { StorefrontModals } from "@/components/StorefrontModals";
Expand All @@ -14,9 +13,8 @@ export default function StorefrontLayout({
}) {
return (
<SmoothScroll>
<Background />
<Header />
<main className="flex-1">{children}</main>
<main className="flex-1 pt-[4.75rem] sm:pt-[5.5rem] md:pt-[5.75rem]">{children}</main>
<Footer />
<StorefrontModals />
<Suspense fallback={null}>
Expand Down
5 changes: 5 additions & 0 deletions Client/app/(storefront)/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { NotFoundMaze } from "@/features/not-found-maze";

export default function StorefrontNotFound() {
return <NotFoundMaze />;
}
34 changes: 18 additions & 16 deletions maze/app/(storefront)/page.tsx → Client/app/(storefront)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,20 @@ export default async function Home() {
]);

return (
<div className="pb-8">
<div className="pb-20">
<HomeHero />

<div className="my-6">
<div className="py-5">
<Marquee brands={brands} />
</div>

<section className="container-x mt-20">
<section id="features" className="mt-0 scroll-mt-28">
<Features features={features} />
</section>

<section className="container-x mt-24">
<section className="container-x mt-28">
<SectionHeading
eyebrow="Каталог MAZE"
eyebrow="Directions"
title="Выбери направление"
subtitle="Оригинальная техника ведущих брендов — от смартфонов до звука и умного дома."
href="/catalog"
Expand All @@ -52,44 +52,46 @@ export default async function Home() {
<CategoryGrid categories={categories} />
</section>

<section className="container-x mt-24">
<section className="container-x mt-28">
<SectionHeading
eyebrow="Хиты продаж"
eyebrow="Editor’s choice"
title="Выбор редакции"
subtitle="Техника, которую забирают чаще всего."
href="/catalog"
/>
<div className="grid w-full grid-cols-2 gap-2 sm:gap-4 lg:grid-cols-4 [grid-template-columns:minmax(0,1fr)_minmax(0,1fr)] lg:[grid-template-columns:repeat(4,minmax(0,1fr))]">
<div className="grid w-full grid-cols-2 gap-3 sm:gap-5 lg:grid-cols-4 [grid-template-columns:minmax(0,1fr)_minmax(0,1fr)] lg:[grid-template-columns:repeat(4,minmax(0,1fr))]">
{hits.map((p, i) => (
<Reveal key={p.id} delay={(i % 4) * 0.06} className="min-w-0 max-w-full">
<Reveal key={p.id} delay={(i % 4) * 0.05} className="min-w-0 max-w-full">
<ProductCard product={p} />
</Reveal>
))}
</div>
</section>

<section className="container-x mt-24">
<Promo />
<section className="mt-28">
<div className="container-x">
<Promo />
</div>
</section>

{fresh.length > 0 && (
<section className="container-x mt-24">
<section className="container-x mt-28">
<SectionHeading
eyebrow="Только приехали"
eyebrow="New arrivals"
title="Новинки"
href="/catalog"
/>
<div className="grid w-full grid-cols-2 gap-2 sm:gap-4 lg:grid-cols-4 [grid-template-columns:minmax(0,1fr)_minmax(0,1fr)] lg:[grid-template-columns:repeat(4,minmax(0,1fr))]">
<div className="grid w-full grid-cols-2 gap-3 sm:gap-5 lg:grid-cols-4 [grid-template-columns:minmax(0,1fr)_minmax(0,1fr)] lg:[grid-template-columns:repeat(4,minmax(0,1fr))]">
{fresh.map((p, i) => (
<Reveal key={p.id} delay={(i % 4) * 0.06} className="min-w-0 max-w-full">
<Reveal key={p.id} delay={(i % 4) * 0.05} className="min-w-0 max-w-full">
<ProductCard product={p} />
</Reveal>
))}
</div>
</section>
)}

<section className="container-x mt-24">
<section id="reviews" className="container-x mt-28 scroll-mt-28">
<Testimonials reviews={reviews} />
</section>
</div>
Expand Down
File renamed without changes.
File renamed without changes.
Loading