Files
shop-server/docs/superpowers/plans/2026-05-19-info-page-static.md
T
2026-05-19 15:32:45 +05:00

18 KiB
Raw Blame History

Static Info Page Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Replace admin-managed dynamic InfoPageBlock CRUD with a hardcoded static React page featuring process schemas, delivery cards, and payment info.

Architecture: New InfoPage.tsx container composes four hardcoded section components (no API calls, no DB reads). All admin CRUD files, server routes, Prisma model, and entity layer are removed.

Tech Stack: React + TypeScript + MUI, lucide-react icons.


File Structure

Created

  • client/src/pages/info/ui/sections/HowToOrderSection.tsx — Stepper with 5 purchase steps
  • client/src/pages/info/ui/sections/DeliverySection.tsx — 3 delivery option cards in Grid
  • client/src/pages/info/ui/sections/PaymentSection.tsx — List of payment methods
  • client/src/pages/info/ui/sections/ReturnsSection.tsx — Returns & warranty Paper blocks

Modified

  • client/src/pages/info/ui/InfoPage.tsx — Rewrite as static container without useQuery
  • client/src/pages/admin-layout/ui/AdminLayoutPage.tsx — Remove info page nav item and import
  • server/src/routes/api.js — Remove import and registration call
  • server/prisma/schema.prisma — Remove InfoPageBlock model

Deleted

  • client/src/pages/admin-info/ui/AdminInfoPage.tsx
  • client/src/pages/admin-info/index.ts
  • client/src/entities/info/api/info-page-api.ts
  • client/src/entities/info/model/types.ts
  • client/src/entities/info/index.ts
  • server/src/routes/api/info-page.js

Task 1: Create HowToOrderSection

Files:

  • Create: client/src/pages/info/ui/sections/HowToOrderSection.tsx

  • Step 1: Write the component

import Paper from "@mui/material/Paper";
import Step from "@mui/material/Step";
import StepContent from "@mui/material/StepContent";
import StepLabel from "@mui/material/StepLabel";
import Stepper from "@mui/material/Stepper";
import Typography from "@mui/material/Typography";
import {
  CheckCircle,
  ClipboardList,
  Mail,
  ShoppingCart,
  Truck,
} from "lucide-react";

const steps = [
  {
    label: "Выберите товары",
    icon: <ShoppingCart size={20} />,
    text: "Найдите нужные изделия в каталоге и добавьте их в корзину. Вы можете выбрать несколько товаров от разных мастеров — все они соберутся в одном заказе.",
  },
  {
    label: "Проверьте корзину",
    icon: <ClipboardList size={20} />,
    text: "Перейдите в корзину и проверьте состав заказа: названия товаров, количество и итоговую сумму. Здесь же можно изменить количество или удалить позиции.",
  },
  {
    label: "Укажите контакты и адрес",
    icon: <Mail size={20} />,
    text: "Заполните имя, телефон и email для связи. Укажите адрес доставки — город, улицу, дом и квартиру. Эти данные нужны для расчёта стоимости и сроков.",
  },
  {
    label: "Выберите доставку и оплату",
    icon: <Truck size={20} />,
    text: "Выберите способ доставки: самовывоз, курьер или почта/СДЭК. Затем укажите способ оплаты: картой онлайн или при получении.",
  },
  {
    label: "Подтвердите заказ",
    icon: <CheckCircle size={20} />,
    text: "Проверьте все данные ещё раз и нажмите «Оформить заказ». После этого мастер получит уведомление и начнёт подготовку вашего изделия.",
  },
];

export function HowToOrderSection() {
  return (
    <Paper variant="outlined" sx={{ p: 3, borderRadius: 2 }}>
      <Typography variant="h5" gutterBottom>
        Как оформить заказ
      </Typography>
      <Stepper orientation="vertical" activeStep={-1}>
        {steps.map((step, idx) => (
          <Step key={idx} completed={false}>
            <StepLabel StepIconComponent={() => step.icon}>
              {step.label}
            </StepLabel>
            <StepContent>
              <Typography color="text.secondary">{step.text}</Typography>
            </StepContent>
          </Step>
        ))}
      </Stepper>
    </Paper>
  );
}
  • Step 2: Commit
git add client/src/pages/info/ui/sections/HowToOrderSection.tsx
git commit -m "feat: add HowToOrderSection with purchase step stepper"

Task 2: Create DeliverySection

Files:

  • Create: client/src/pages/info/ui/sections/DeliverySection.tsx

  • Step 1: Write the component

import Grid from "@mui/material/Grid";
import Paper from "@mui/material/Paper";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import { Package, Store, Truck } from "lucide-react";
import { PICKUP_ADDRESS_FULL } from "@/shared/constants/pickup-point";

const deliveries = [
  {
    title: "Самовывоз",
    icon: <Store size={28} />,
    lines: [
      "Бесплатно.",
      PICKUP_ADDRESS_FULL,
      "Перед визитом согласуем время — чтобы заказ точно был готов к выдаче.",
    ],
  },
  {
    title: "Курьер по городу",
    icon: <Truck size={28} />,
    lines: [
      "Доставка в пределах города.",
      "Сроки и стоимость зависят от адреса и веса заказа.",
      "Мастер свяжется с вами для уточнения деталей после оформления.",
    ],
  },
  {
    title: "Почта / СДЭК",
    icon: <Package size={28} />,
    lines: [
      "Отправка в другие города.",
      "Каждому заказу присваивается трек-номер для отслеживания.",
      "Стоимость рассчитывается по тарифу перевозчика при оформлении.",
    ],
  },
];

export function DeliverySection() {
  return (
    <Paper variant="outlined" sx={{ p: 3, borderRadius: 2 }}>
      <Typography variant="h5" gutterBottom>
        Доставка
      </Typography>
      <Grid container spacing={2}>
        {deliveries.map((d) => (
          <Grid key={d.title} size={{ xs: 12, sm: 6, md: 4 }}>
            <Paper
              variant="outlined"
              sx={{ p: 2, borderRadius: 2, height: "100%" }}
            >
              <Stack spacing={1.5}>
                <Stack
                  direction="row"
                  spacing={1}
                  sx={{ alignItems: "center" }}
                >
                  {d.icon}
                  <Typography variant="h6">{d.title}</Typography>
                </Stack>
                {d.lines.map((line, i) => (
                  <Typography key={i} variant="body2" color="text.secondary">
                    {line}
                  </Typography>
                ))}
              </Stack>
            </Paper>
          </Grid>
        ))}
      </Grid>
    </Paper>
  );
}
  • Step 2: Commit
git add client/src/pages/info/ui/sections/DeliverySection.tsx
git commit -m "feat: add DeliverySection with pickup, courier, and postal cards"

Task 3: Create PaymentSection

Files:

  • Create: client/src/pages/info/ui/sections/PaymentSection.tsx

  • Step 1: Write the component

import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import ListItemIcon from "@mui/material/ListItemIcon";
import ListItemText from "@mui/material/ListItemText";
import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography";
import { Banknote, CreditCard } from "lucide-react";

const methods = [
  {
    icon: <CreditCard size={22} />,
    primary: "Банковская карта онлайн",
    secondary:
      "Оплата картой Visa, Mastercard или МИР сразу при оформлении заказа.",
  },
  {
    icon: <Banknote size={22} />,
    primary: "Оплата при получении",
    secondary: "Оплата наличными или картой при получении заказа.",
  },
];

export function PaymentSection() {
  return (
    <Paper variant="outlined" sx={{ p: 3, borderRadius: 2 }}>
      <Typography variant="h5" gutterBottom>
        Оплата
      </Typography>
      <Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
        Оплата происходит после подтверждения заказа мастером. Вы получите
        уведомление, когда заказ будет подтверждён и готов к оплате.
      </Typography>
      <List disablePadding>
        {methods.map((m) => (
          <ListItem key={m.primary} disableGutters>
            <ListItemIcon sx={{ minWidth: 40 }}>{m.icon}</ListItemIcon>
            <ListItemText primary={m.primary} secondary={m.secondary} />
          </ListItem>
        ))}
      </List>
    </Paper>
  );
}
  • Step 2: Commit
git add client/src/pages/info/ui/sections/PaymentSection.tsx
git commit -m "feat: add PaymentSection with card and cash methods"

Task 4: Create ReturnsSection

Files:

  • Create: client/src/pages/info/ui/sections/ReturnsSection.tsx

  • Step 1: Write the component

import Paper from "@mui/material/Paper";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";

export function ReturnsSection() {
  return (
    <Paper variant="outlined" sx={{ p: 3, borderRadius: 2 }}>
      <Typography variant="h5" gutterBottom>
        Возврат и гарантии
      </Typography>
      <Stack spacing={2}>
        <Paper variant="outlined" sx={{ p: 2, borderRadius: 2 }}>
          <Typography variant="subtitle1" sx={{ fontWeight: 600 }} gutterBottom>
            Возврат
          </Typography>
          <Typography variant="body2" color="text.secondary">
            Если товар не соответствует описанию или имеет производственный
            дефект, свяжитесь с нами в течение 7 дней после получения. Мы
            заменим изделие на аналогичное или вернём деньги. Возврат товара
            надлежащего качества возможен в течение 14 дней, если изделие не
            было в употреблении и сохранён его товарный вид.
          </Typography>
        </Paper>
        <Paper variant="outlined" sx={{ p: 2, borderRadius: 2 }}>
          <Typography variant="subtitle1" sx={{ fontWeight: 600 }} gutterBottom>
            Гарантия качества
          </Typography>
          <Typography variant="body2" color="text.secondary">
            Мы отвечаем за качество каждого изделия ручной работы. Все дефекты,
            возникшие не по вине покупателя, устраняются или компенсируются
            заменой изделия. Если у вас возникли вопросы по качеству  напишите
            нам, и мы решим проблему в кратчайшие сроки.
          </Typography>
        </Paper>
      </Stack>
    </Paper>
  );
}
  • Step 2: Commit
git add client/src/pages/info/ui/sections/ReturnsSection.tsx
git commit -m "feat: add ReturnsSection with return and warranty blocks"

Task 5: Rewrite InfoPage as static container

Files:

  • Modify: client/src/pages/info/ui/InfoPage.tsx

  • Step 1: Rewrite InfoPage.tsx

import Box from "@mui/material/Box";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import { DeliverySection } from "./sections/DeliverySection";
import { HowToOrderSection } from "./sections/HowToOrderSection";
import { PaymentSection } from "./sections/PaymentSection";
import { ReturnsSection } from "./sections/ReturnsSection";

export function InfoPage() {
  return (
    <Box>
      <Typography variant="h4" gutterBottom>
        Информация для покупателей
      </Typography>
      <Typography color="text.secondary" sx={{ mb: 3 }}>
        Как оформить заказ, как проходит доставка, оплата и другие важные
        детали.
      </Typography>

      <Stack spacing={3}>
        <HowToOrderSection />
        <DeliverySection />
        <PaymentSection />
        <ReturnsSection />
      </Stack>
    </Box>
  );
}
  • Step 2: Commit
git add client/src/pages/info/ui/InfoPage.tsx
git commit -m "feat: rewrite InfoPage as static container with section components"

Task 6: Delete admin info page

Files:

  • Delete: client/src/pages/admin-info/ui/AdminInfoPage.tsx

  • Delete: client/src/pages/admin-info/index.ts

  • Step 1: Delete files

rm client/src/pages/admin-info/ui/AdminInfoPage.tsx
rm client/src/pages/admin-info/index.ts
  • Step 2: Commit
git add client/src/pages/admin-info/
git commit -m "feat: remove admin info page CRUD"

Task 7: Delete entities/info

Files:

  • Delete: client/src/entities/info/api/info-page-api.ts

  • Delete: client/src/entities/info/model/types.ts

  • Delete: client/src/entities/info/index.ts

  • Step 1: Delete files

rm client/src/entities/info/api/info-page-api.ts
rm client/src/entities/info/model/types.ts
rm client/src/entities/info/index.ts
  • Step 2: Commit
git add client/src/entities/info/
git commit -m "feat: remove info entity (admin CRUD layer)"

Task 8: Clean up AdminLayoutPage

Files:

  • Modify: client/src/pages/admin-layout/ui/AdminLayoutPage.tsx

  • Step 1: Remove import

// REMOVE line 23:
import { AdminInfoPage } from "@/pages/admin-info";

Execute this edit: In client/src/pages/admin-layout/ui/AdminLayoutPage.tsx, remove the import line:

import { AdminInfoPage } from "@/pages/admin-info";
  • Step 2: Remove FileText from lucide-react import

In line 18, change:

import {
  Bell,
  FileText,
  Image,
  LayoutGrid,
  ListOrdered,
  MessageSquare,
  Store,
  Users,
} from "lucide-react";

to:

import {
  Bell,
  Image,
  LayoutGrid,
  ListOrdered,
  MessageSquare,
  Store,
  Users,
} from "lucide-react";
  • Step 3: Remove nav item

Remove the nav item entry (line 64):

{ to: '/admin/info', label: 'Инфо-страница', icon: <FileText /> },
  • Step 4: Remove route

Remove the route line 192:

<Route path="info" element={<AdminInfoPage />} />
  • Step 5: Commit
git add client/src/pages/admin-layout/ui/AdminLayoutPage.tsx
git commit -m "feat: remove info page from admin navigation and routes"

Task 9: Delete server info-page routes

Files:

  • Delete: server/src/routes/api/info-page.js

  • Modify: server/src/routes/api.js

  • Step 1: Delete the routes file

rm server/src/routes/api/info-page.js
  • Step 2: Clean up api.js

In server/src/routes/api.js:

Remove the import line 10:

import { registerInfoPageRoutes } from "./api/info-page.js";

Remove the call line 21:

await registerInfoPageRoutes(fastify);
  • Step 3: Commit
git add server/src/routes/api/info-page.js server/src/routes/api.js
git commit -m "feat: remove server info-page routes"

Task 10: Remove InfoPageBlock model from Prisma schema

Files:

  • Modify: server/prisma/schema.prisma

  • Step 1: Remove model from schema

Remove lines 262-273 from server/prisma/schema.prisma:

model InfoPageBlock {
  id        String   @id @default(cuid())
  key       String   @unique
  title     String
  body      String
  sort      Int      @default(0)
  published Boolean  @default(true)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([published, sort])
}

Also remove the blank line before it (line 261).

  • Step 2: Run migration
cd server && npm run db:migrate

Expected: Prisma creates a new migration dropping the InfoPageBlock table.

  • Step 3: Commit
git add server/prisma/schema.prisma server/prisma/migrations/
git commit -m "feat: remove InfoPageBlock model from Prisma schema"

Task 11: Verify build and lint

  • Step 1: Run server tests
cd server && npm test

Expected: all tests pass (no info-page tests exist, but other tests should still pass after removing routes).

  • Step 2: Run client lint
cd client && npm run lint

Expected: no errors.

  • Step 3: Run client format check
cd client && npm run format:check

Expected: all files formatted.

  • Step 4: Run client tests
cd client && npm test

Expected: all tests pass.

  • Step 5: Build client
cd client && npm run build

Expected: tsc + Vite build succeed with no errors.

  • Step 6: Commit if any fixes
git add -A
git commit -m "chore: lint and build fixes after info page migration"