Skip to content

Conversation

kodermax
Copy link
Contributor

@kodermax kodermax commented Aug 12, 2025

Summary by CodeRabbit

  • Новые возможности

    • В письмах о создании заказа каждому товару теперь отображается цена с копейками; при настроенном SITE_URL в письмах появляется ссылка на страницу успешного оформления.
    • API товаров возвращает расширённый набор полей (цены, статусы, артикулы, SEO-поля, изображения и др.), давая больше данных для отображения.
    • У бренда добавлено поле banner (по умолчанию null).
    • validateDraft теперь в ответе включает поле data с подробностями валидации.
  • Исправления

    • Адрес покупателя сохраняется только если явно указан флаг сохранения, предотвращая неверную привязку.

Copy link
Contributor

coderabbitai bot commented Aug 12, 2025

Warning

Rate limit exceeded

@kodermax has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 13 minutes and 17 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 5453dc3 and f99f460.

📒 Files selected for processing (12)
  • docs/checkout-drafts-anonymous-users.md (1 hunks)
  • docs/examples/checkout-draft-usage-updated.md (1 hunks)
  • packages/api/src/router/orders/create.ts (2 hunks)
  • packages/api/src/router/product-attribute-values/upsert.ts (2 hunks)
  • packages/config/package.json (1 hunks)
  • packages/config/src/env.ts (1 hunks)
  • packages/emails/send.ts (2 hunks)
  • packages/web-api/src/lib/draft-utils.ts (3 hunks)
  • packages/web-api/src/router/account/orders/get-orders.ts (3 hunks)
  • packages/web-api/src/router/brands/get-featured.ts (3 hunks)
  • packages/web-api/src/router/category/get-by-slug.ts (1 hunks)
  • packages/web-api/src/router/products/get-popular-by-category.ts (1 hunks)
📝 Walkthrough

Walkthrough

В create-order вычисляется orderUrl из env.SITE_URL и передаётся в OrderCreatedEmail; в payload orderItems добавлено поле price. Добавлен пакет @qco/config (экспорт env); удалён локальный packages/emails/env.ts и обновлены импорты env в пакете emails.

Changes

Cohort / File(s) Summary of Changes
Order creation (web-api)
packages/web-api/src/router/orders/create-order.ts
Импорт env переключён на @qco/config; вычисляется orderUrl = env.SITE_URL ? \${env.SITE_URL}/checkout/success?orderId=${order.id}` : undefined; в payload orderItemsдобавленоprice(форматированная строка);OrderCreatedEmailвызывается с новым аргументомorderUrl`.
Config package added
packages/config/src/env.ts, packages/config/src/index.ts, packages/config/package.json, packages/config/tsup.config.ts, packages/config/tsconfig.json
Новый пакет @qco/config экспортирует env (создан через createEnv/zod). Добавлены package.json с root-exports, tsup/tsconfig сборочно-типовые файлы.
Emails package — env relocation & imports
packages/emails/env.ts (removed), packages/emails/index.ts, packages/emails/send.ts, packages/emails/tsup.config.ts, packages/emails/package.json, packages/emails/emails/*
Удалён локальный packages/emails/env.ts; в многочисленных email-компонентах и send.ts импорт env переключён на @qco/config. В packages/emails/index.ts удалён экспорт env. tsup.config.ts теперь использует process.env.NODE_ENV. Обновлены зависимости (добавлен @qco/config, удалены @t3-oss/env-core и zod).
Products API responses (web-api)
packages/web-api/src/router/products/get-new.ts, .../get-sale.ts, .../get-popular-by-category.ts
Расширены возвращаемые поля продуктов (brandId, isActive, isFeatured, isPopular, isNew, stock, sku, basePrice, salePrice, discountPercent, hasVariants, productTypeId, createdAt/updatedAt, seo* поля, xmlId, image/images и т.п.). Поведение по отсутствующим значениям сделано null/nullable-ориентированным; удалено onSale; в get-new добавлена фильтрация по categorySlug.
Profile creation safety
packages/web-api/src/router/profile/create-profile-from-order.ts
Создание адреса выполняется только если saveAddress === true и newCustomer был создан; возвращаемый customerId использует optional chaining (newCustomer?.id).
Brands / Categories / Misc API shape tweaks
packages/web-api/src/router/brands/get-by-id.ts, .../get-featured.ts, packages/web-api/src/router/categories/get-all.ts, packages/web-api/src/router/category/get-by-slug.ts
Добавлено поле banner: null в брендах; переход на isNull в запросах категорий; явная проекция полей в get-by-slug, imageKey с null-fallback; локальные касты/ослабления типов.
Typing/local utilities & minor API changes
packages/web-api/src/lib/draft-utils.ts, packages/web-api/src/router/account/orders/*.ts, packages/api/src/router/*, packages/web-api/src/router/checkout/validate-draft.ts, packages/web-api/src/router/products/get-popular-by-category.ts
Локальные аннотации типов (часто any), переход к nullish/coalescing, в validate-draft вход draftData: z.record(z.string(), z.any()).optional() и в успешном ответе добавлено data: validationResult.data. Возвращаемые поля в некоторых маршрутах стали строками/ISO (например, totalAmount как string, cancelledAt обработан через ?.).
Emails — order email API surface
@qco/emails types / usages
Публичная сигнатура OrderCreatedEmail расширена: добавлено опциональное поле orderUrl. В payload элементов теперь присутствует price — изменение структуры передаваемых данных.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant API as Web API (create-order)
  participant Config as @qco/config
  participant Emails as @qco/emails
  participant User

  Client->>API: POST /orders
  API->>API: Создать заказ, собрать orderItems (включая price)
  API->>Config: Чтение env.SITE_URL
  API->>API: orderUrl = env.SITE_URL ? `${env.SITE_URL}/checkout/success?orderId=${order.id}` : undefined
  API->>Emails: OrderCreatedEmail({ order, orderItems, orderUrl })
  Emails-->>User: Отправка письма (включает per-item price и ссылку если есть)
  API-->>Client: 200 OK (данные заказа)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Possibly related PRs

  • up #10: Модифицирует тот же файл packages/web-api/src/router/orders/create-order.ts — пересечение по формированию email при создании заказа.
  • Dev #17: Меняет packages/web-api/src/router/orders/create-order.ts (внесение данных в OrderCreatedEmail, например subject/номер заказа) — потенциальный конфликт сигнатур/пейлоадов.
  • up #3: Трогает маппинг items в create-order.ts (удаление/изменение полей items) — возможная конфликтующая правка структуры items (attributes vs price).
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (2)
packages/web-api/src/router/orders/create-order.ts (2)

105-108: Ошибки при отправке email проглатываются — добавьте хотя бы логирование

Полное игнорирование исключений осложнит поддержку и диагностику инцидентов. Предлагаю залогировать предупреждение с ключевым контекстом (замените на ваш логгер при наличии).

       } catch (emailError) {
-
-        // Не прерываем выполнение процедуры, если email не удалось отправить
+        // Не прерываем выполнение процедуры, если email не удалось отправить,
+        // но логируем для диагностики (замените console.warn на ваш логгер).
+        console.warn("Failed to send OrderCreatedEmail", {
+          orderId: order.id,
+          to: customerInfo.email,
+          error: emailError,
+        });
       }

70-70: as any убирает типобезопасность

В этой точке теряется гарантия типов на элементы корзины. Лучше описать явный DTO для items или расширить возвращаемые типы createOrder, чтобы привести мэппинг к строгой типизации без any.

Готов помочь выделить общий тип для CartItemDTO и убрать приведение к any по всей цепочке.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a065c14 and 4433fcf.

📒 Files selected for processing (1)
  • packages/web-api/src/router/orders/create-order.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

**/*.{ts,tsx}: Use strict typing in TypeScript and avoid any.
Use environment variables or @t3-oss/env-core for experimental feature flags.

Files:

  • packages/web-api/src/router/orders/create-order.ts
{packages/web-api/**,packages/api/**,packages/web-validators/**,packages/validators/**}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

Use the same Zod schemas on both frontend and backend. Import schemas from /packages/web-validators (for web) and /packages/validators (for app) to guarantee unified validation and type safety.

Files:

  • packages/web-api/src/router/orders/create-order.ts
{packages/web-api/**,packages/api/**}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

The server must prepare and return clear, frontend-ready data structures. All necessary transformations and formatting should be performed on the server, so that the frontend receives data in the exact shape it needs.

Files:

  • packages/web-api/src/router/orders/create-order.ts
{packages/web-api/**,packages/api/**,packages/lib/**}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

Keep business logic decoupled in /packages/web-api, /packages/api, or /packages/lib.

Files:

  • packages/web-api/src/router/orders/create-order.ts
**/*.ts

📄 CodeRabbit Inference Engine (.windsurfrules)

Use Zod for validation.

Files:

  • packages/web-api/src/router/orders/create-order.ts

⚙️ CodeRabbit Configuration File

**/*.ts: ОСНОВНЫЕ ПРИНЦИПЫ РАЗРАБОТКИ:

  1. Именование и стиль кода:

    • Имена файлов обязательно в kebab-case (например: my-component.ts, user-profile.tsx)
    • Функции в camelCase, компоненты в PascalCase, константы в UPPER_SNAKE_CASE
    • Имена переменных и функций должны четко отражать их назначение и реализацию
    • Избегайте сокращений и аббревиатур в именах (кроме общепринятых)
    • Поддерживайте консистентность в стиле кода (отступы, пробелы, переносы строк)
    • Следуйте принципам чистого кода (DRY, SOLID, KISS)
    • Используйте осмысленные имена для булевых переменных (isLoading, hasPermission, canEdit)
  2. TypeScript и типизация:

    • Используйте строгую типизацию и избегайте any, unknown и {} где это возможно
    • Правильно применяйте дженерики, интерфейсы и типы
    • Обеспечивайте корректные определения типов для параметров функций и возвращаемых значений
    • Предпочитайте union и intersection типы сложным условным конструкциям
    • Используйте утилитарные типы TypeScript (Pick, Omit, Partial, Required)
    • Создавайте переиспользуемые типы и интерфейсы
    • Избегайте дублирования типов, выносите общие типы в отдельные файлы
  3. Соблюдение правил линтера:

    • Следуйте правилам ESLint для React и TypeScript
    • Соблюдайте рекомендации Biome по форматированию и анализу кода
    • Убедитесь, что код не содержит предупреждений или ошибок линтера
    • Используйте автоматическое форматирование кода
  4. Алгоритмы и производительность:

    • Пишите понятные и читаемые алгоритмы, которые могут понять другие разработчики
    • Оптимизируйте сложность алгоритмов (временную и пространственную)
    • Избегайте чрезмерной вложенности и сложности
    • Правильно обрабатывайте граничные случаи и ошибки
    • Используйте подходящие структуры данных для задач
    • Применяйте мемоизацию где это уместно
  5. Безопасность и стабильность:

    • Реализуйте правильную валидацию входных данных и защиту от инъекций
    • Корректно обрабатывайте ошибки и исключения
    • Обеспечивайте...

Files:

  • packages/web-api/src/router/orders/create-order.ts
🔇 Additional comments (1)
packages/web-api/src/router/orders/create-order.ts (1)

90-101: Проверка orderUrl: опциональный проп с дефолтом

В OrderCreatedEmailProps поле orderUrl объявлено как опциональное (orderUrl?: string), а в компоненте задаётся значение по умолчанию (orderUrl = defaultOrderUrl). Передача orderUrl всегда безопасна, дополнительных правок не требуется.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🔭 Outside diff range comments (3)
packages/emails/emails/orders/order-created.tsx (2)

15-19: Возможная некорректная ссылка при отсутствующем SITE_URL: получится "undefined/orders"

Если env.SITE_URL не задан, defaultOrderUrl станет строкой "undefined/orders" и попадёт в письмо как битая ссылка. Нормализуйте базовый URL и избегайте генерации ссылки в этом случае.

Предлагаю правку:

-import { env } from "@qco/config";
+import { env } from "@qco/config";
@@
-const defaultOrderUrl = `${env.SITE_URL}/orders`;
+const defaultOrderUrl =
+  env.SITE_URL && env.SITE_URL.trim().length > 0
+    ? `${env.SITE_URL.replace(/\/+$/, "")}/orders`
+    : undefined;

151-158: Кнопка с ссылкой рендерится без проверки наличия orderUrl

При пустом SITE_URL из конфига (или не переданном orderUrl) кнопка будет вести на undefined/orders. Рендерите кнопку только при валидной ссылке.

Примените условный рендер:

-            <Section className="mb-[32px] mt-[32px] text-center">
-              <Button
-                className="rounded bg-[#000000] px-6 py-3 text-center text-[14px] font-semibold text-white no-underline hover:bg-gray-800"
-                href={orderUrl}
-              >
-                Посмотреть детали заказа
-              </Button>
-            </Section>
+            {orderUrl && (
+              <Section className="mb-[32px] mt-[32px] text-center">
+                <Button
+                  className="rounded bg-[#000000] px-6 py-3 text-center text-[14px] font-semibold text-white no-underline hover:bg-gray-800"
+                  href={orderUrl}
+                >
+                  Посмотреть детали заказа
+                </Button>
+              </Section>
+            )}
packages/emails/send.ts (1)

60-76: Унифицировать отправку письма через клиента Resend в sendEmailHtml

  • Файл: packages/emails/send.ts (функция sendEmailHtml, строки 60–76)
    sendEmailHtml сейчас делает прямой fetch с заголовком Bearer ${env.RESEND_API_KEY}, даже если ключ не задан. Предлагаю заменить на использование уже созданного клиента resend и добавить ранний выход при отсутствии ключа (как в sendEmail).

Было:

export const sendEmailHtml = async (email: EmailHtml) => {
  const from = email.from || env.EMAIL_FROM || DEFAULT_FROM_EMAIL;

  await fetch("https://api.resend.com/emails", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${env.RESEND_API_KEY}`,
    },
    body: JSON.stringify({
      to: email.to,
      from,
      subject: email.subject,
      html: email.html,
    }),
  });
};

Стало:

export const sendEmailHtml = async (email: EmailHtml) => {
  const from = email.from || env.EMAIL_FROM || DEFAULT_FROM_EMAIL;
- await fetch("https://api.resend.com/emails", {
-   method: "POST",
-   headers: {
-     "Content-Type": "application/json",
-     Authorization: `Bearer ${env.RESEND_API_KEY}`,
-   },
-   body: JSON.stringify({
-     to: email.to,
-     from,
-     subject: email.subject,
-     html: email.html,
-   }),
- });
+ if (!resend) {
+   return Promise.resolve();
+ }
+ return resend.emails.send({
+   to: email.to,
+   from,
+   subject: email.subject,
+   html: email.html,
+ });
};

Так мы:

  1. Избежим Bearer undefined при отсутствии ключа.
  2. Используем единый механизм отправки через клиент Resend.
♻️ Duplicate comments (3)
packages/emails/emails/auth/email-change-verification.tsx (1)

15-15: Импорт env из @qco/config — корректно

Хорошая консолидация конфигурации. Смотри также комментарий в email-verification.tsx о проверке зависимости и экспортов пакета, чтобы не дублировать здесь.

packages/web-api/src/router/orders/create-order.ts (2)

7-7: Импорт env из @qco/config — архитектурно верно

Развязывает слой web-api от @qco/emails и устраняет риск циклических зависимостей. Хорошее изменение.


81-83: Собирайте orderUrl через URL API и кодируйте orderId; избегайте двойных слэшей

Текущая конкатенация даёт // при SITE_URL со слэшем на конце и не кодирует orderId. Используйте URL API.

Примените дифф:

-        const orderUrl = env.SITE_URL
-          ? `${env.SITE_URL}/checkout/success?orderId=${order.id}`
-          : undefined;
+        const orderUrl = env.SITE_URL
+          ? (() => {
+              const u = new URL("/checkout/success", env.SITE_URL);
+              u.searchParams.set("orderId", String(order.id));
+              return u.toString();
+            })()
+          : undefined;
🧹 Nitpick comments (6)
packages/emails/emails/welcome.tsx (1)

17-17: Несоответствие нейминга: username используется как имя и как email

В тексте письма username выводится как имя ("Здравствуйте, {username}!"), но далее — как адрес email ("письмо отправлено на адрес {username}"). Разделите пропсы на username и email либо скорректируйте текст.

Пример:

export default function WelcomeEmail(
  { username = "username", email }: { username: string; email: string }
) {
  ...
  <Text className="text-[12px] leading-[24px] text-[#666666]">
    Это письмо отправлено на адрес <span className="text-black">{email}</span>.
  </Text>
}
packages/emails/emails/auth/verification-otp.tsx (2)

15-15: Задайте явный тип возврата getVerificationInfo и обеспечьте исчерпывающую проверку

Это улучшит типобезопасность при расширении union-типа и исключит silent-undefined в будущем.

Пример:

type VerificationInfo = {
  title: string;
  preview: string;
  description: string;
  validity: string;
};

const getVerificationInfo = (type: VerificationType): VerificationInfo => {
  switch (type) {
    case "sign-in":
      return { ... };
    case "email-verification":
      return { ... };
    case "forget-password":
      return { ... };
    default: {
      // исчерпывающая проверка на уровне типов
      const _exhaustive: never = type;
      throw new Error(`Unknown verification type: ${_exhaustive}`);
    }
  }
};

15-15: Нитпик: литерал "forget-password"

Если это не связано с публичным API, рассмотрите "forgot-password" для естественного английского. В противном случае оставьте как есть для обратной совместимости.

packages/emails/emails/orders/order-created.tsx (1)

104-113: Незначительно: ключи элементов списка

Использование индекса в качестве ключа допустимо для статических списков, но при равных именах товаров и возможных изменениях порядка лучше использовать стабильный ключ (например, сочетание name + quantity), чтобы избежать потенциальных артефактов рендеринга.

-              {items?.map((item, index) => (
-                <div key={index} className="mb-[12px] pb-[12px] border-b border-gray-200 last:border-b-0">
+              {items?.map((item) => (
+                <div key={`${item.name}-${item.quantity}`} className="mb-[12px] pb-[12px] border-b border-gray-200 last:border-b-0">
packages/emails/emails/auth/password-reset.tsx (1)

19-26: Небольшая несогласованность нейминга: username используется как адрес email

В тексте: приветствие как имени пользователя и далее «Это письмо отправлено на адрес {username}». Если здесь подразумевается адрес, рассмотрите переименование пропа в email и отдельный проп для отображаемого имени.

-export default function PasswordResetEmail({
-  username = "username",
-  resetLink,
-}: {
-  username: string;
-  resetLink: string;
-}) {
+export default function PasswordResetEmail({
+  username = "username",
+  email,
+  resetLink,
+}: {
+  username: string;
+  email: string;
+  resetLink: string;
+}) {
@@
-              Это письмо отправлено на адрес{" "}
-              <span className="text-black">{username}</span>. Если вы не
+              Это письмо отправлено на адрес{" "}
+              <span className="text-black">{email}</span>. Если вы не

Also applies to: 66-69

packages/config/tsup.config.ts (1)

5-11: Зафиксируйте platform/target/outDir для стабильной сборки

Небольшой апгрейд сборки для предсказуемых артефактов и будущей совместимости.

Примените дифф:

 export default defineConfig({
     entry: ["src/index.ts"],
     format: ["esm"],
     dts: true,
     splitting: false,
     sourcemap: false,
     clean: true,
     minify: false,
+    platform: "node",
+    target: "node18",
+    outDir: "dist",
     external: [
         "@t3-oss/env-core",
         "zod"
     ],
 });
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4433fcf and 7c6e234.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • packages/config/package.json (1 hunks)
  • packages/config/src/env.ts (1 hunks)
  • packages/config/src/index.ts (1 hunks)
  • packages/config/tsconfig.json (1 hunks)
  • packages/config/tsup.config.ts (1 hunks)
  • packages/emails/emails/admin-invitation.tsx (1 hunks)
  • packages/emails/emails/auth/email-change-verification.tsx (1 hunks)
  • packages/emails/emails/auth/email-verification.tsx (1 hunks)
  • packages/emails/emails/auth/otp-sign-in.tsx (1 hunks)
  • packages/emails/emails/auth/password-reset.tsx (1 hunks)
  • packages/emails/emails/auth/verification-otp.tsx (1 hunks)
  • packages/emails/emails/orders/order-created.tsx (1 hunks)
  • packages/emails/emails/orders/order-status-updated.tsx (1 hunks)
  • packages/emails/emails/orders/order-updated.tsx (1 hunks)
  • packages/emails/emails/welcome.tsx (1 hunks)
  • packages/emails/env.ts (0 hunks)
  • packages/emails/index.ts (1 hunks)
  • packages/emails/package.json (1 hunks)
  • packages/emails/send.ts (1 hunks)
  • packages/emails/tsup.config.ts (1 hunks)
  • packages/web-api/src/router/orders/create-order.ts (3 hunks)
💤 Files with no reviewable changes (1)
  • packages/emails/env.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/emails/emails/orders/order-updated.tsx
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

**/*.{ts,tsx}: Use strict typing in TypeScript and avoid any.
Use environment variables or @t3-oss/env-core for experimental feature flags.

Files:

  • packages/config/src/env.ts
  • packages/config/src/index.ts
  • packages/emails/emails/auth/email-change-verification.tsx
  • packages/emails/emails/auth/email-verification.tsx
  • packages/emails/emails/orders/order-created.tsx
  • packages/emails/tsup.config.ts
  • packages/emails/emails/auth/verification-otp.tsx
  • packages/emails/emails/orders/order-status-updated.tsx
  • packages/emails/index.ts
  • packages/emails/emails/auth/otp-sign-in.tsx
  • packages/emails/emails/auth/password-reset.tsx
  • packages/config/tsup.config.ts
  • packages/emails/emails/welcome.tsx
  • packages/emails/send.ts
  • packages/web-api/src/router/orders/create-order.ts
  • packages/emails/emails/admin-invitation.tsx
**/*.ts

📄 CodeRabbit Inference Engine (.windsurfrules)

Use Zod for validation.

Files:

  • packages/config/src/env.ts
  • packages/config/src/index.ts
  • packages/emails/tsup.config.ts
  • packages/emails/index.ts
  • packages/config/tsup.config.ts
  • packages/emails/send.ts
  • packages/web-api/src/router/orders/create-order.ts

⚙️ CodeRabbit Configuration File

**/*.ts: ОСНОВНЫЕ ПРИНЦИПЫ РАЗРАБОТКИ:

  1. Именование и стиль кода:

    • Имена файлов обязательно в kebab-case (например: my-component.ts, user-profile.tsx)
    • Функции в camelCase, компоненты в PascalCase, константы в UPPER_SNAKE_CASE
    • Имена переменных и функций должны четко отражать их назначение и реализацию
    • Избегайте сокращений и аббревиатур в именах (кроме общепринятых)
    • Поддерживайте консистентность в стиле кода (отступы, пробелы, переносы строк)
    • Следуйте принципам чистого кода (DRY, SOLID, KISS)
    • Используйте осмысленные имена для булевых переменных (isLoading, hasPermission, canEdit)
  2. TypeScript и типизация:

    • Используйте строгую типизацию и избегайте any, unknown и {} где это возможно
    • Правильно применяйте дженерики, интерфейсы и типы
    • Обеспечивайте корректные определения типов для параметров функций и возвращаемых значений
    • Предпочитайте union и intersection типы сложным условным конструкциям
    • Используйте утилитарные типы TypeScript (Pick, Omit, Partial, Required)
    • Создавайте переиспользуемые типы и интерфейсы
    • Избегайте дублирования типов, выносите общие типы в отдельные файлы
  3. Соблюдение правил линтера:

    • Следуйте правилам ESLint для React и TypeScript
    • Соблюдайте рекомендации Biome по форматированию и анализу кода
    • Убедитесь, что код не содержит предупреждений или ошибок линтера
    • Используйте автоматическое форматирование кода
  4. Алгоритмы и производительность:

    • Пишите понятные и читаемые алгоритмы, которые могут понять другие разработчики
    • Оптимизируйте сложность алгоритмов (временную и пространственную)
    • Избегайте чрезмерной вложенности и сложности
    • Правильно обрабатывайте граничные случаи и ошибки
    • Используйте подходящие структуры данных для задач
    • Применяйте мемоизацию где это уместно
  5. Безопасность и стабильность:

    • Реализуйте правильную валидацию входных данных и защиту от инъекций
    • Корректно обрабатывайте ошибки и исключения
    • Обеспечивайте...

Files:

  • packages/config/src/env.ts
  • packages/config/src/index.ts
  • packages/emails/tsup.config.ts
  • packages/emails/index.ts
  • packages/config/tsup.config.ts
  • packages/emails/send.ts
  • packages/web-api/src/router/orders/create-order.ts
**/*.json

⚙️ CodeRabbit Configuration File

**/*.json: РАБОТА С JSON ФАЙЛАМИ:

  1. Структура и форматирование:

    • Используйте правильное форматирование JSON
    • Поддерживайте консистентную структуру
    • Используйте осмысленные ключи
    • Избегайте дублирования данных
  2. Валидация:

    • Убедитесь в корректности JSON синтаксиса
    • Проверяйте типы данных
    • Валидируйте обязательные поля
  3. Безопасность:

    • Не включайте конфиденциальные данные
    • Используйте переменные окружения для секретов
    • Валидируйте входящие JSON данные

Files:

  • packages/config/tsconfig.json
  • packages/config/package.json
  • packages/emails/package.json
**/*.{tsx,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/trpc.mdc)

**/*.{tsx,jsx}: When using tRPC with React Query, always generate query options using trpc.<namespace>.<procedure>.queryOptions() and pass them to useQuery instead of calling the procedure directly.
When using tRPC mutations with React Query, always generate mutation options using trpc.<namespace>.<procedure>.mutationOptions() and pass them to useMutation instead of calling the procedure directly.
After a successful mutation that affects a list, invalidate the relevant query cache using queryClient.invalidateQueries with the correct query key from tRPC.
Display user feedback (such as toast notifications) on mutation success or error using the toast function.

Files:

  • packages/emails/emails/auth/email-change-verification.tsx
  • packages/emails/emails/auth/email-verification.tsx
  • packages/emails/emails/orders/order-created.tsx
  • packages/emails/emails/auth/verification-otp.tsx
  • packages/emails/emails/orders/order-status-updated.tsx
  • packages/emails/emails/auth/otp-sign-in.tsx
  • packages/emails/emails/auth/password-reset.tsx
  • packages/emails/emails/welcome.tsx
  • packages/emails/emails/admin-invitation.tsx
**/*.{jsx,tsx}

📄 CodeRabbit Inference Engine (.windsurfrules)

**/*.{jsx,tsx}: Use Lucide React for icons throughout the application.
Use TanStack Query (react-query) for frontend data fetching.
Use React Hook Form for form handling.
Use React Context for state management.
Use the useTRPC hook for accessing tRPC procedures.
Use queryClient.invalidateQueries for cache invalidation after mutations.

Files:

  • packages/emails/emails/auth/email-change-verification.tsx
  • packages/emails/emails/auth/email-verification.tsx
  • packages/emails/emails/orders/order-created.tsx
  • packages/emails/emails/auth/verification-otp.tsx
  • packages/emails/emails/orders/order-status-updated.tsx
  • packages/emails/emails/auth/otp-sign-in.tsx
  • packages/emails/emails/auth/password-reset.tsx
  • packages/emails/emails/welcome.tsx
  • packages/emails/emails/admin-invitation.tsx
**/*.tsx

⚙️ CodeRabbit Configuration File

**/*.tsx: ПРИНЦИПЫ РАЗРАБОТКИ REACT КОМПОНЕНТОВ:

  1. Именование и стиль кода:

    • Имена файлов обязательно в kebab-case (например: my-component.tsx, user-profile.tsx)
    • Компоненты в PascalCase, функции в camelCase, константы в UPPER_SNAKE_CASE
    • Имена компонентов должны четко отражать их назначение и функциональность
    • Избегайте сокращений в именах компонентов
    • Поддерживайте консистентность в стиле кода
    • Следуйте принципам чистого кода (DRY, SOLID, KISS)
  2. TypeScript и типизация:

    • Используйте строгую типизацию для всех пропсов компонентов
    • Избегайте any, unknown и {} где это возможно
    • Правильно типизируйте события и колбэки
    • Используйте дженерики для переиспользуемых компонентов
    • Создавайте интерфейсы для пропсов компонентов
    • Типизируйте состояние компонентов
    • Используйте утилитарные типы React (ComponentProps, PropsWithChildren, etc.)
  3. React и Next.js лучшие практики:

    • Оптимизируйте React компоненты (используйте мемоизацию, поддерживайте правильную структуру)
    • Правильно используйте хуки (useEffect, useState, useCallback, useMemo)
    • Предотвращайте утечки памяти в useEffect
    • Корректно реализуйте серверные и клиентские компоненты в Next.js
    • Оптимизируйте стратегии маршрутизации и загрузки данных (SSR, SSG, ISR)
    • Используйте React.memo для предотвращения ненужных ре-рендеров
    • Правильно управляйте зависимостями в useEffect
    • Избегайте создания объектов и функций в рендере
  4. Производительность компонентов:

    • Оптимизируйте рендеринг списков с помощью виртуализации при необходимости
    • Используйте lazy loading для компонентов
    • Минимизируйте количество ре-рендеров
    • Правильно используйте key пропы в списках
    • Избегайте встроенных объектов и функций в JSX
    • Используйте Code Splitting для больших компонентов
  5. Управление состоянием:

    • Поднимайте состояние только когда это необходимо
    • Используйте локальное состояние где это возможно
    • Правильно структурируйте сост...

Files:

  • packages/emails/emails/auth/email-change-verification.tsx
  • packages/emails/emails/auth/email-verification.tsx
  • packages/emails/emails/orders/order-created.tsx
  • packages/emails/emails/auth/verification-otp.tsx
  • packages/emails/emails/orders/order-status-updated.tsx
  • packages/emails/emails/auth/otp-sign-in.tsx
  • packages/emails/emails/auth/password-reset.tsx
  • packages/emails/emails/welcome.tsx
  • packages/emails/emails/admin-invitation.tsx
**/package.json

⚙️ CodeRabbit Configuration File

**/package.json: УПРАВЛЕНИЕ ЗАВИСИМОСТЯМИ:

  1. Версионирование:

    • Используйте семантическое версионирование
    • Избегайте уязвимых версий пакетов
    • Регулярно обновляйте зависимости
  2. Структура:

    • Правильно организуйте scripts
    • Разделяйте dependencies и devDependencies
    • Используйте точные версии для критических пакетов
  3. Безопасность:

    • Проверяйте пакеты на уязвимости
    • Используйте только проверенные пакеты
    • Регулярно аудируйте зависимости

Files:

  • packages/config/package.json
  • packages/emails/package.json
{packages/web-api/**,packages/api/**,packages/web-validators/**,packages/validators/**}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

Use the same Zod schemas on both frontend and backend. Import schemas from /packages/web-validators (for web) and /packages/validators (for app) to guarantee unified validation and type safety.

Files:

  • packages/web-api/src/router/orders/create-order.ts
{packages/web-api/**,packages/api/**}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

The server must prepare and return clear, frontend-ready data structures. All necessary transformations and formatting should be performed on the server, so that the frontend receives data in the exact shape it needs.

Files:

  • packages/web-api/src/router/orders/create-order.ts
{packages/web-api/**,packages/api/**,packages/lib/**}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

Keep business logic decoupled in /packages/web-api, /packages/api, or /packages/lib.

Files:

  • packages/web-api/src/router/orders/create-order.ts
🧠 Learnings (2)
📚 Learning: 2025-08-01T07:57:26.911Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/code.mdc:0-0
Timestamp: 2025-08-01T07:57:26.911Z
Learning: Applies to **/*.{ts,tsx} : Use environment variables or `t3-oss/env-core` for experimental feature flags.

Applied to files:

  • packages/config/src/env.ts
  • packages/config/src/index.ts
  • packages/emails/tsup.config.ts
  • packages/config/tsup.config.ts
  • packages/emails/package.json
📚 Learning: 2025-08-01T07:58:22.450Z
Learnt from: CR
PR: qcohq/qco#0
File: .windsurfrules:0-0
Timestamp: 2025-08-01T07:58:22.450Z
Learning: Applies to packages/db/**/*.{ts,js} : Database schemas are defined in `qco/db`.

Applied to files:

  • packages/config/tsconfig.json
  • packages/config/package.json
🧬 Code Graph Analysis (1)
packages/web-api/src/router/orders/create-order.ts (3)
packages/config/src/env.ts (1)
  • env (4-27)
packages/emails/emails/orders/admin-order-notification.tsx (2)
  • AdminOrderNotification (39-166)
  • item (89-98)
packages/emails/emails/orders/order-cancelled.tsx (1)
  • OrderCancelledEmail (20-69)
🔇 Additional comments (13)
packages/config/tsconfig.json (1)

1-8: LGTM! Корректная конфигурация TypeScript для внутреннего пакета.

Конфигурация правильно использует базовую настройку для внутренних пакетов и корректно включает директорию src. Структура соответствует стандартам монорепозитория.

packages/emails/package.json (1)

30-30: LGTM! Зависимости корректно реорганизованы.

Удаление @t3-oss/env-core и zod оправдано, поскольку эти зависимости теперь инкапсулированы в новом пакете @qco/config. Зависимость tailwindcss-animate корректно сохранена.

packages/emails/tsup.config.ts (1)

3-3: LGTM! Правильное использование переменных окружения.

Переход к использованию process.env.NODE_ENV напрямую является корректным решением, особенно в контексте миграции к централизованному пакету конфигурации. Это упрощает логику сборки и устраняет зависимость от локального модуля env.

packages/emails/emails/orders/order-status-updated.tsx (1)

15-15: LGTM! Корректная миграция на централизованную конфигурацию.

Переход к использованию @qco/config для импорта переменных окружения соответствует архитектурному решению о централизации конфигурации. Использование env.SITE_URL на строке 18 остается неизменным, что обеспечивает обратную совместимость.

packages/emails/emails/admin-invitation.tsx (1)

17-17: LGTM! Корректная миграция на централизованную конфигурацию.

Импорт env из @qco/config соответствует общей стратегии миграции в этом PR. Использование env.SITE_NAME в компоненте (строки 40 и 50) остается неизменным, что обеспечивает функциональную совместимость.

packages/emails/emails/auth/verification-otp.tsx (1)

15-15: Импорт env из @qco/config — ок

Миграция на общий конфиг соответствует архитектурным целям и не влияет на существующую разметку.

packages/config/src/index.ts (1)

1-1: Экспорт env — корректно и минималистично

Публичная поверхность пакета ровно то, что нужно для потребителей.

packages/emails/index.ts (1)

18-18: Импорт env из @qco/emails не найден — всё в порядке

Локальный поиск подтвердил отсутствие:

  • import { env } from '@qco/emails' в других пакетах
  • относительных импортов from './env', from '../env', from '../../env' в packages/emails
packages/emails/emails/orders/order-created.tsx (1)

15-15: Централизация env: импорт из @qco/config — ок

Переход на общий конфиг — правильный шаг для консистентности.

packages/emails/send.ts (1)

7-7: Импорт env из @qco/config — ок, проверьте зависимости

Актуализируйте зависимости packages/emails на @qco/config и убедитесь, что бандлер правильно резолвит внутренний пакет.

Запустите скрипт проверки (аналогичный из email-verification.tsx), он покажет наличие зависимости и экспортов.

packages/emails/emails/auth/password-reset.tsx (1)

17-17: Импорт env из @qco/config — хорошо

Консолидация конфигурации выглядит корректно. Совместимость типов env.SITE_NAME с рендерингом — ок.

packages/config/tsup.config.ts (1)

3-15: Связка tsup ↔ exports: убедитесь, что пакет указывает на dist, а не на src

Сборка настроена корректно, но сейчас package.json (см. мой комментарий там) экспортирует ./src/index.ts. Это приведёт к импорту TS-исходников потребителями вместо скомпилированного JS из dist. Выровняйте exports на dist и типы — иначе возможны рантайм-проблемы.

packages/web-api/src/router/orders/create-order.ts (1)

94-94: Проверено: OrderCreatedEmail принимает опциональный пропс orderUrl
В интерфейсе OrderCreatedEmailProps поле orderUrl объявлено с ? и в компоненте задано значение по умолчанию, значит пропс опционален.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (10)
packages/web-api/src/router/profile/create-profile-from-order.ts (5)

75-86: Не возвращайте undefined в customerId при success

newCustomer?.id может быть undefined (например, если .returning() не вернул строку). Возвращать success: true без customerId сломает клиентов. Гарантируйте, что newCustomer не пустой, иначе кидайте ошибку.

Примените проверку после вставки и уберите optional chaining:

-            }).returning();
+            }).returning();
+            if (!newCustomer) {
+                throw new TRPCError({
+                    code: "INTERNAL_SERVER_ERROR",
+                    message: "Не удалось создать профиль (БД вернула пустой результат)",
+                });
+            }
-                customerId: newCustomer?.id,
+                customerId: newCustomer.id,

Also applies to: 107-111


75-106: Сделайте создание клиента и адреса атомарным (транзакция)

Если вставка адреса упадёт, клиент уже будет создан (неатомарность). Оберните создание клиента и, при необходимости, адреса в одну транзакцию.

Примерно так (псевдокод, синтаксис зависит от используемой ORM):

await ctx.db.transaction(async (tx) => {
  const [newCustomer] = await tx.insert(customers).values({...}).returning();
  if (!newCustomer) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Не удалось создать профиль" });

  if (input.saveAddress) {
    await tx.insert(customerAddresses).values({
      id: createId(),
      customerId: newCustomer.id,
      // ...
    });
  }
});

26-30: Гонка при проверке e-mail и вставке — используйте upsert/обработку уникального ограничения

Между проверкой существования и вставкой возможна гонка, которая приведёт к уникальному конфликту по email.

Решения:

  • Использовать upsert (on conflict do update returning) по уникальному ключу email.
  • Либо ловить ошибку уникальности на вставке и выполнять обновление.

Если используете drizzle-orm (Postgres):

const [row] = await ctx.db
  .insert(customers)
  .values(values)
  .onConflictDoUpdate({
    target: customers.email,
    set: {
      firstName: input.firstName,
      lastName: input.lastName,
      phone: input.phone,
      isGuest: false,
      updatedAt: new Date(),
    },
  })
  .returning();

Also applies to: 72-86


58-60: Проверьте единственность «дефолтного» адреса клиента

В текущей схеме в таблице customer_addresses есть лишь обычный индекс на поле is_default, но нет уникального ограничения на пару (customer_id, is_default=true). При каждом вставлении новой записи с isDefault: true могут накопиться несколько «дефолтных» адресов.

Решения (выберите подходящее или комбинацию):

  • Добавить на уровне БД частичный уникальный индекс:
    CREATE UNIQUE INDEX uq_customer_default_address ON customer_addresses(customer_id) WHERE is_default = true;
  • В коде перед вставкой сбрасывать флаг у предыдущих дефолтных адресов:
    await ctx.db
      .update(customerAddresses)
      .set({ isDefault: false })
      .where(eq(customerAddresses.customerId, customerId));
    затем вставлять новый с isDefault: true.

Затронутые места:

  • packages/web-api/src/router/profile/create-profile-from-order.ts
    • линии 47–60 (вставка адреса для обновления клиента)
    • линии 89–102 (вставка адреса для нового клиента)
  • packages/web-api/src/lib/orders/order-helpers.ts
    • линии 165–182 (сохранение дефолтного адреса при создании заказа)

22-24: Ограничить доступ к createProfileFromOrder (использовать protectedProcedure + проверка email)

Текущая реализация на publicProcedure позволяет любому с известным e-mail изменить чужой профиль и снять флаг isGuest. Необходимо:

• В packages/web-api/src/router/profile/create-profile-from-order.ts
– заменить
import { publicProcedure } from "../../trpc";
на
import { protectedProcedure } from "../../trpc";
– заменить
export const createProfileFromOrder = publicProcedure
на
export const createProfileFromOrder = protectedProcedure
– добавить в начале мутации проверку, что ctx.session.user.email === input.email, и выбрасывать TRPCError с кодом FORBIDDEN при несовпадении.

Пример diff:

-import { publicProcedure } from "../../trpc";
+import { protectedProcedure } from "../../trpc";-export const createProfileFromOrder = publicProcedure
+export const createProfileFromOrder = protectedProcedure
   .input(createProfileFromOrderSchema)
   .mutation(async ({ ctx, input }) => {
+    if (!ctx.session?.user?.email || ctx.session.user.email !== input.email) {
+      throw new TRPCError({ code: "FORBIDDEN", message: "Недостаточно прав для изменения профиля" });
+    }
     try {
       // …
packages/web-api/src/router/products/get-sale.ts (2)

70-79: Избегайте использования any в TypeScript

Использование типа any нарушает строгую типизацию и может привести к ошибкам во время выполнения. Следует определить правильные типы для product и других переменных.

Определите правильные типы вместо any:

interface ProductWithRelations {
  id: string;
  brandId: string | null;
  name: string;
  slug: string;
  // ... остальные поля
  categories?: Array<{
    category?: {
      id: string;
      name: string;
      slug: string;
    };
  }>;
  files?: Array<{
    file: {
      id: string;
      path: string;
    };
  }>;
  brand?: {
    id: string;
    name: string;
    slug: string;
  };
  // ... остальные поля
}

const filteredProducts = productsData.filter((product: ProductWithRelations) => {
  return product.categories?.some((pc) =>
    pc.category?.slug === categorySlug
  );
});

120-126: Неинформативная обработка ошибок

Блок catch перехватывает ошибку, но не логирует её и не предоставляет контекстную информацию. Это затрудняет отладку в production.

Добавьте логирование ошибки и передачу более детальной информации:

 } catch (error) {
+    console.error('Error fetching sale products:', error);
+    const errorMessage = error instanceof Error ? error.message : 'Unknown error';
 
     throw new TRPCError({
         code: "INTERNAL_SERVER_ERROR",
-        message: "Failed to fetch sale products",
+        message: `Failed to fetch sale products: ${errorMessage}`,
+        cause: error,
     });
 }
packages/web-api/src/router/products/get-new.ts (3)

75-79: Использование any нарушает строгую типизацию

Аналогично файлу get-sale.ts, здесь также используется тип any, что противоречит рекомендациям по строгой типизации.

Определите правильные типы для всех переменных вместо использования any. См. рекомендации для файла get-sale.ts.


124-130: Недостаточная обработка ошибок

Аналогично get-sale.ts, здесь также отсутствует логирование ошибки и детальная информация об ошибке.

Добавьте логирование и более информативное сообщение об ошибке:

 } catch (error) {
+    console.error('Error fetching new products:', error);
+    const errorMessage = error instanceof Error ? error.message : 'Unknown error';
 
     throw new TRPCError({
         code: "INTERNAL_SERVER_ERROR",
-        message: "Failed to fetch new products",
+        message: `Failed to fetch new products: ${errorMessage}`,
+        cause: error,
     });
 }

22-22: Исправить несоответствие slug для категории “women”
В get-sale.ts используется women: "zhenschinam", а в get-new.ts — women: "zhenskaya-odezhda". Такое расхождение приведёт к разным результатам для одного и того же раздела.

Пожалуйста, выберите один корректный slug (например, тот, что хранится в БД или уже используется на фронте) и приведите к нему оба эндпоинта:

• packages/web-api/src/router/products/get-sale.ts (строка с women: "zhenschinam")
• packages/web-api/src/router/products/get-new.ts (строка с women: "zhenskaya-odezhda")

После этого проверьте, что на фронтенде все вызовы (use-active-root-category, ProductGrid, Brand… и т.п.) используют тот же slug.

🧹 Nitpick comments (4)
packages/web-api/src/router/profile/create-profile-from-order.ts (2)

73-75: Не используйте устаревший substr при генерации customerCode

substr устарел. Замените на slice (или используйте более надёжный генератор).

-const customerCode = `CUST-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
+const customerCode = `CUST-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;

Опционально: используйте createId()/nanoid для кода, если важна предсказуемая уникальность и длина.


47-63: Дублирование логики вставки адреса — вынесите в helper

Код создания адреса повторяется для существующего и нового клиента. Это затрудняет обслуживание и повышает риск расхождений.

Вынесите сбор значений в функцию/константу и переиспользуйте:

const buildAddressValues = (customerId: string) => ({
  id: createId(),
  customerId,
  type: "shipping",
  firstName: input.firstName,
  lastName: input.lastName,
  addressLine1: input.address,
  addressLine2: input.apartment,
  city: input.city,
  state: input.state,
  postalCode: input.postalCode,
  country: "Россия",
  isDefault: true,
  createdAt: new Date(),
  updatedAt: new Date(),
});

Затем:

await ctx.db.insert(customerAddresses).values(buildAddressValues(existingCustomer.id));

и

await ctx.db.insert(customerAddresses).values(buildAddressValues(newCustomer.id));

Also applies to: 90-104

packages/web-api/src/router/products/get-sale.ts (1)

98-99: Последовательное использование проверки на null

Для basePrice и salePrice используется проверка != null, в то время как для других полей используется оператор ??. Рекомендуется использовать единообразный подход.

Для единообразия можно использовать оператор ?? и с приведением типа:

-basePrice: product.basePrice != null ? Number(product.basePrice) : null,
-salePrice: product.salePrice != null ? Number(product.salePrice) : null,
+basePrice: product.basePrice ? Number(product.basePrice) : null,
+salePrice: product.salePrice ? Number(product.salePrice) : null,
packages/web-api/src/router/products/get-new.ts (1)

102-103: Несогласованная проверка на null

Используется != null для проверки, что может быть заменено на более идиоматичный подход с ?? оператором для консистентности с остальным кодом.

См. рекомендацию для аналогичного кода в get-sale.ts.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c6e234 and 55127e8.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • packages/auth/env.ts (0 hunks)
  • packages/emails/package.json (2 hunks)
  • packages/web-api/package.json (1 hunks)
  • packages/web-api/src/router/products/get-new.ts (2 hunks)
  • packages/web-api/src/router/products/get-sale.ts (2 hunks)
  • packages/web-api/src/router/profile/create-profile-from-order.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • packages/auth/env.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/web-api/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/emails/package.json
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

**/*.{ts,tsx}: Use strict typing in TypeScript and avoid any.
Use environment variables or @t3-oss/env-core for experimental feature flags.

Files:

  • packages/web-api/src/router/profile/create-profile-from-order.ts
  • packages/web-api/src/router/products/get-sale.ts
  • packages/web-api/src/router/products/get-new.ts
{packages/web-api/**,packages/api/**,packages/web-validators/**,packages/validators/**}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

Use the same Zod schemas on both frontend and backend. Import schemas from /packages/web-validators (for web) and /packages/validators (for app) to guarantee unified validation and type safety.

Files:

  • packages/web-api/src/router/profile/create-profile-from-order.ts
  • packages/web-api/src/router/products/get-sale.ts
  • packages/web-api/src/router/products/get-new.ts
{packages/web-api/**,packages/api/**}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

The server must prepare and return clear, frontend-ready data structures. All necessary transformations and formatting should be performed on the server, so that the frontend receives data in the exact shape it needs.

Files:

  • packages/web-api/src/router/profile/create-profile-from-order.ts
  • packages/web-api/src/router/products/get-sale.ts
  • packages/web-api/src/router/products/get-new.ts
{packages/web-api/**,packages/api/**,packages/lib/**}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

Keep business logic decoupled in /packages/web-api, /packages/api, or /packages/lib.

Files:

  • packages/web-api/src/router/profile/create-profile-from-order.ts
  • packages/web-api/src/router/products/get-sale.ts
  • packages/web-api/src/router/products/get-new.ts
**/*.ts

📄 CodeRabbit Inference Engine (.windsurfrules)

Use Zod for validation.

Files:

  • packages/web-api/src/router/profile/create-profile-from-order.ts
  • packages/web-api/src/router/products/get-sale.ts
  • packages/web-api/src/router/products/get-new.ts

⚙️ CodeRabbit Configuration File

**/*.ts: ОСНОВНЫЕ ПРИНЦИПЫ РАЗРАБОТКИ:

  1. Именование и стиль кода:

    • Имена файлов обязательно в kebab-case (например: my-component.ts, user-profile.tsx)
    • Функции в camelCase, компоненты в PascalCase, константы в UPPER_SNAKE_CASE
    • Имена переменных и функций должны четко отражать их назначение и реализацию
    • Избегайте сокращений и аббревиатур в именах (кроме общепринятых)
    • Поддерживайте консистентность в стиле кода (отступы, пробелы, переносы строк)
    • Следуйте принципам чистого кода (DRY, SOLID, KISS)
    • Используйте осмысленные имена для булевых переменных (isLoading, hasPermission, canEdit)
  2. TypeScript и типизация:

    • Используйте строгую типизацию и избегайте any, unknown и {} где это возможно
    • Правильно применяйте дженерики, интерфейсы и типы
    • Обеспечивайте корректные определения типов для параметров функций и возвращаемых значений
    • Предпочитайте union и intersection типы сложным условным конструкциям
    • Используйте утилитарные типы TypeScript (Pick, Omit, Partial, Required)
    • Создавайте переиспользуемые типы и интерфейсы
    • Избегайте дублирования типов, выносите общие типы в отдельные файлы
  3. Соблюдение правил линтера:

    • Следуйте правилам ESLint для React и TypeScript
    • Соблюдайте рекомендации Biome по форматированию и анализу кода
    • Убедитесь, что код не содержит предупреждений или ошибок линтера
    • Используйте автоматическое форматирование кода
  4. Алгоритмы и производительность:

    • Пишите понятные и читаемые алгоритмы, которые могут понять другие разработчики
    • Оптимизируйте сложность алгоритмов (временную и пространственную)
    • Избегайте чрезмерной вложенности и сложности
    • Правильно обрабатывайте граничные случаи и ошибки
    • Используйте подходящие структуры данных для задач
    • Применяйте мемоизацию где это уместно
  5. Безопасность и стабильность:

    • Реализуйте правильную валидацию входных данных и защиту от инъекций
    • Корректно обрабатывайте ошибки и исключения
    • Обеспечивайте...

Files:

  • packages/web-api/src/router/profile/create-profile-from-order.ts
  • packages/web-api/src/router/products/get-sale.ts
  • packages/web-api/src/router/products/get-new.ts
🧠 Learnings (7)
📚 Learning: 2025-08-01T07:58:07.363Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/web-api.mdc:0-0
Timestamp: 2025-08-01T07:58:07.363Z
Learning: Applies to apps/web/**/*.tsx : Import Drizzle ORM functions like eq, sql from qco/db, e.g., import { eq, sql } from "qco/db";

Applied to files:

  • packages/web-api/src/router/products/get-sale.ts
  • packages/web-api/src/router/products/get-new.ts
📚 Learning: 2025-08-01T07:57:06.890Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/api.mdc:0-0
Timestamp: 2025-08-01T07:57:06.890Z
Learning: Applies to packages/api/**/*.ts : Import functions like eq, sql from qco/db, e.g., import { eq, sql } from "qco/db";

Applied to files:

  • packages/web-api/src/router/products/get-sale.ts
  • packages/web-api/src/router/products/get-new.ts
📚 Learning: 2025-08-01T07:58:22.450Z
Learnt from: CR
PR: qcohq/qco#0
File: .windsurfrules:0-0
Timestamp: 2025-08-01T07:58:22.450Z
Learning: Applies to packages/db/**/*.{ts,js} : Database schemas are defined in `qco/db`.

Applied to files:

  • packages/web-api/src/router/products/get-sale.ts
  • packages/web-api/src/router/products/get-new.ts
📚 Learning: 2025-08-01T07:58:07.363Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/web-api.mdc:0-0
Timestamp: 2025-08-01T07:58:07.363Z
Learning: Applies to apps/web/**/*.tsx : Import table schemas from qco/db/schema, e.g., import { Category, ProductCategory } from "qco/db/schema";

Applied to files:

  • packages/web-api/src/router/products/get-sale.ts
  • packages/web-api/src/router/products/get-new.ts
📚 Learning: 2025-08-01T07:57:06.890Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/api.mdc:0-0
Timestamp: 2025-08-01T07:57:06.890Z
Learning: Applies to packages/api/**/*.ts : Import schemas from qco/db/schema, e.g., import { Category, ProductCategory } from "qco/db/schema";

Applied to files:

  • packages/web-api/src/router/products/get-sale.ts
  • packages/web-api/src/router/products/get-new.ts
📚 Learning: 2025-08-01T07:58:07.363Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/web-api.mdc:0-0
Timestamp: 2025-08-01T07:58:07.363Z
Learning: Applies to apps/web/**/*.tsx : Use ctx for database access in tRPC routes to ensure consistent data handling

Applied to files:

  • packages/web-api/src/router/products/get-sale.ts
  • packages/web-api/src/router/products/get-new.ts
📚 Learning: 2025-08-01T07:57:06.890Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/api.mdc:0-0
Timestamp: 2025-08-01T07:57:06.890Z
Learning: Applies to packages/api/**/*.ts : Use TRPCError for consistent error handling across all routes.

Applied to files:

  • packages/web-api/src/router/products/get-sale.ts
  • packages/web-api/src/router/products/get-new.ts
🧬 Code Graph Analysis (2)
packages/web-api/src/router/profile/create-profile-from-order.ts (2)
apps/web/src/features/checkout/components/checkout-page-client.tsx (1)
  • data (137-163)
packages/web-api/src/lib/orders/order-helpers.ts (1)
  • CreateOrderParams (53-65)
packages/web-api/src/router/products/get-new.ts (1)
apps/web/src/features/products/components/enhanced-catalog-page-new.tsx (1)
  • EnhancedCatalogPage (32-241)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test API Package
🔇 Additional comments (3)
packages/web-api/src/router/profile/create-profile-from-order.ts (1)

88-88: LGTM: добавлена проверка наличия newCustomer при сохранении адреса

Хорошая защита от обращения к undefined при вставке адреса.

packages/web-api/src/router/products/get-sale.ts (1)

2-2: Неиспользуемый импорт asc

Функция asc импортируется, но не используется в этом файле. На строке 62 используется asc(productFiles.order), но это вызов метода объекта asc, а не импортированной функции.

Удалите неиспользуемый импорт:

-import { eq, and, desc, isNotNull, lt, asc } from "@qco/db";
+import { eq, and, desc, isNotNull, lt } from "@qco/db";
⛔ Skipped due to learnings
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/api.mdc:0-0
Timestamp: 2025-08-01T07:57:06.890Z
Learning: Applies to packages/api/**/*.ts : Import functions like eq, sql from qco/db, e.g., import { eq, sql } from "qco/db";
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/web-api.mdc:0-0
Timestamp: 2025-08-01T07:58:07.363Z
Learning: Applies to apps/web/**/*.tsx : Import Drizzle ORM functions like eq, sql from qco/db, e.g., import { eq, sql } from "qco/db";
Learnt from: CR
PR: qcohq/qco#0
File: .windsurfrules:0-0
Timestamp: 2025-08-01T07:58:22.450Z
Learning: Applies to packages/db/**/*.{ts,js} : Database schemas are defined in `qco/db`.
packages/web-api/src/router/products/get-new.ts (1)

16-17: Неиспользуемая логика с cutoffDate

Переменная cutoffDate вычисляется, но не используется, так как условие на строке 33 закомментировано. Это создаёт путаницу относительно логики работы функции.

Нужно определиться с логикой: либо использовать дату для фильтрации новых товаров, либо удалить неиспользуемый код:

-// Вычисляем дату, начиная с которой товары считаются новыми
-const cutoffDate = new Date();
-cutoffDate.setDate(cutoffDate.getDate() - days);

// ... в where условии:
 where: and(
     eq(products.isActive, true),
     eq(products.isNew, true),
-    //gte(products.createdAt, cutoffDate)
+    gte(products.createdAt, cutoffDate)
 ),

Или полностью удалить логику с датой, если она не нужна.

Also applies to: 33-33

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🔭 Outside diff range comments (11)
packages/web-api/src/router/category/get-by-slug.ts (2)

26-28: Используйте TRPCError(NOT_FOUND) вместо Error для консистентности ошибок API

Это обеспечит корректные HTTP-коды и единый формат обработки ошибок на клиенте.

Примените дифф:

-            throw new Error(`Категория со slug "${input.slug}" не найдена`);
+            throw new TRPCError({
+                code: 'NOT_FOUND',
+                message: `Категория со slug "${input.slug}" не найдена`,
+            });

1-4: Добавьте импорт TRPCError

Необходим для предыдущей правки обработки ошибок.

Примените дифф:

 import { eq } from '@qco/db'
 import { categories } from '@qco/db/schema'
 import { categorySlugInputV2, categorySchemaV2 } from '@qco/web-validators'
+import { TRPCError } from '@trpc/server'
 import { publicProcedure } from '../../trpc'
packages/web-api/src/router/products/get-popular-by-category.ts (2)

54-59: Избавиться от any, сузить выборку и дедуплицировать productIds

  • Не нужно тащить with: { product: true }, если вам нужен только productId.
  • Уберите any и упростите типизацию.
  • Дедуплицируйте productIds, чтобы не плодить повторов в IN-выборке.
  • Привяжите маппинг к конкретному типу (без any), используя локальные интерфейсы.
-    const productCategoriesData = await db.query.productCategories.findMany({
-      where: eq(productCategories.categoryId, category.id),
-      with: {
-        product: true,
-      },
-    });
+    const productCategoriesData = await db.query.productCategories.findMany({
+      where: eq(productCategories.categoryId, category.id),
+      columns: {
+        productId: true,
+      },
+    });
-    const productIds = productCategoriesData.map((pc: any) => pc.productId);
+    const productIds = Array.from(
+      new Set(productCategoriesData.map((pc) => pc.productId)),
+    );
-    return productsData.map((product: any) => {
+    return productsData.map((product: ProductWithRelations) => {

Добавьте рядом с локальными интерфейсами тип результата запроса:

// рядом с ProductFileType / ProductCategoryRelationType
type ProductWithRelations = typeof products.$inferSelect & {
  brand: { name: string } | null;
  files: ProductFileType[];
  categories: ProductCategoryRelationType[];
};

Also applies to: 66-66, 92-92


69-74: Добавить фильтрацию по популярности в get-popular-by-category

В текущей реализации в packages/web-api/src/router/products/get-popular-by-category.ts в блоке where отсутствует условие по полю isPopular, из-за чего возвращаются все активные товары категории, а не только популярные.

• Файл: packages/web-api/src/router/products/get-popular-by-category.ts
• Строки: 69–74

Примените патч:

   const productsData = await db.query.products.findMany({
     where: and(
       inArray(products.id, productIds),
       eq(products.isActive, true),
+      eq(products.isPopular, true),
     ),
     with: {
packages/web-api/src/router/account/orders/get-order-detail.ts (3)

38-43: Использование any не соответствует принципам строгой типизации TypeScript.

Добавление явных типов any для параметров колбеков противоречит кодингайдлайнам, которые требуют строгой типизации и избегания any. Эти типы можно вывести автоматически или определить более точно на основе схемы базы данных.

Примените следующий diff для улучшения типизации:

-      .filter((combination: any) => combination.option && combination.optionValue)
-      .map((combination: any) => ({
+      .filter((combination) => combination.option && combination.optionValue)
+      .map((combination) => ({

Либо создайте правильный тип для комбинации:

type OptionCombination = {
  option: { name: string; slug: string } | null;
  optionValue: { displayName?: string; value: string } | null;
};

20-23: Несогласованность типизации параметра ctx.

Параметр ctx типизирован как any, что нарушает принципы строгой типизации. В других файлах (например, в packages/api/src/router/orders/by-id.ts) используется более точный тип TRPCContext.

Примените следующий diff для унификации типизации:

-async function getVariantOptions(
-  ctx: any,
-  variantId: string
-): Promise<Array<{ name: string; value: string; slug: string }>> {
+async function getVariantOptions(
+  ctx: TRPCContext,
+  variantId: string
+): Promise<Array<{ name: string; value: string; slug: string }>> {

Также добавьте импорт типа:

+import type { TRPCContext } from "../../../trpc";

17-50: Дублирование кода функции getVariantOptions.

Функция getVariantOptions полностью дублируется в нескольких файлах (packages/api/src/router/orders/by-id.ts, packages/api/src/router/orders/list.ts и текущем файле). Это нарушает принцип DRY и усложняет сопровождение кода.

Рекомендуется вынести эту функцию в общую утилиту, например, в packages/lib или создать отдельный модуль в packages/web-api/src/lib/variant-options.ts:

// packages/web-api/src/lib/variant-options.ts
import type { TRPCContext } from "../trpc";
import { eq } from "@qco/db";
import { productVariantOptionCombinations } from "@qco/db/schema";

export async function getVariantOptions(
  ctx: TRPCContext,
  variantId: string
): Promise<Array<{ name: string; value: string; slug: string }>> {
  // реализация функции
}

Затем импортировать и использовать в нужных файлах.

packages/web-api/src/router/account/orders/get-orders.ts (1)

39-44: Использование any противоречит принципам строгой типизации.

Аналогично предыдущему файлу, добавление явных типов any нарушает кодингайдлайны по строгой типизации TypeScript.

Примените следующий diff:

-      .filter((combination: any) => combination.option && combination.optionValue)
-      .map((combination: any) => ({
+      .filter((combination) => combination.option && combination.optionValue)
+      .map((combination) => ({
packages/web-api/src/router/brands/get-by-id.ts (1)

30-33: Используйте TRPCError и не теряйте контекст ошибки

Сейчас 404 превращается в общий "Failed to fetch...". Верните корректный код NOT_FOUND и не заглушайте TRPCError.

Примените дифф:

@@
-      if (!brand) {
-        throw new Error(`Brand with ID "${id}" not found`);
-      }
+      if (!brand) {
+        throw new TRPCError({
+          code: "NOT_FOUND",
+          message: `Brand with ID "${id}" not found`,
+        });
+      }
@@
-    } catch (error) {
-      throw new Error("Failed to fetch brand by ID");
-    }
+    } catch (error) {
+      if (error instanceof TRPCError) throw error;
+      throw new TRPCError({
+        code: "INTERNAL_SERVER_ERROR",
+        message: "Failed to fetch brand by ID",
+      });
+    }

Добавьте импорт:

import { TRPCError } from "@trpc/server";

Also applies to: 62-65

packages/web-api/src/router/checkout/validate-draft.ts (2)

19-26: Унифицируйте структуру ответа (ключи должны быть стабильными)

Сейчас на ошибке возвращаются validation/completeness, а на успехе — data/errors/recommendations (в другом контейнере). Лучше всегда возвращать { success, data, error }, где data либо объект с результатами, либо null.

Примените дифф:

@@
-            return {
-                success: false,
-                error: "Данные для валидации не переданы",
-                validation: null,
-                completeness: null,
-            };
+            return {
+                success: false,
+                data: null,
+                error: "Данные для валидации не переданы",
+            };
@@
-            return {
-                success: true,
-                data: {
-                    isValid: validationResult.isValid,
-                    data: validationResult.data,
-                    errors: validationResult.errors,
-                    recommendations: generateRecommendations(validationResult)
-                },
-                error: null
-            };
+            return {
+                success: true,
+                data: {
+                    isValid: validationResult.isValid,
+                    data: validationResult.data,
+                    errors: validationResult.errors,
+                    recommendations: generateRecommendations(validationResult),
+                },
+                error: null,
+            };
@@
-            return {
-                success: false,
-                error: "Ошибка при валидации данных",
-                validation: null,
-                completeness: null,
-            };
+            return {
+                success: false,
+                data: null,
+                error: "Ошибка при валидации данных",
+            };

Also applies to: 32-41, 45-51


57-83: Типизируйте generateRecommendations без any

Используйте DraftValidationResult и ZodIssue, избавьтесь от any и закрепите тип группы ошибок.

Примените дифф:

-function generateRecommendations(validationResult: any) {
-    const recommendations: string[] = [];
+function generateRecommendations(validationResult: DraftValidationResult) {
+    const recommendations: string[] = [];
@@
-    if (!validationResult.isValid && validationResult.errors) {
-        // Группируем ошибки по типам для рекомендаций
-        const fieldErrors = validationResult.errors.reduce((acc: any, error: any) => {
-            const field = error.path[0];
-            if (!acc[field]) acc[field] = [];
-            acc[field].push(error.message);
-            return acc;
-        }, {});
+    if (!validationResult.isValid && validationResult.errors) {
+        // Группируем ошибки по полям
+        const fieldErrors = validationResult.errors.reduce((acc: Record<string, string[]>, error: ZodIssue) => {
+            const field = String(error.path[0] ?? "general");
+            if (!acc[field]) acc[field] = [];
+            acc[field].push(error.message);
+            return acc;
+        }, {} as Record<string, string[]>);

Добавьте импорты:

import type { DraftValidationResult } from "@qco/web-validators";
import type { ZodIssue } from "zod";
♻️ Duplicate comments (1)
packages/web-api/src/router/account/orders/get-orders.ts (1)

21-51: Дублирование функции getVariantOptions.

Эта функция является точной копией функций из других файлов. Необходимо вынести её в общую утилиту для соблюдения принципа DRY.

См. комментарий к аналогичной функции в файле packages/web-api/src/router/account/orders/get-order-detail.ts.

🧹 Nitpick comments (6)
packages/web-api/src/router/products/get-popular-by-category.ts (3)

91-139: Повторяющийся маппинг товара — вынесите в общий helper/DTO

По описанию PR аналогичный маппинг есть в get-new.ts и get-sale.ts. Поддерживать 3 копии рискованно (расхождение полей, баги). Предлагаю вынести маппер в общий модуль (например, packages/lib/products/mapToListItem.ts) и использовать везде один источник истины.


140-142: Не проглатывайте ошибки — добавьте логирование

Сейчас при исключении вы молча возвращаете пустой массив. Это затрудняет отладку в проде. Минимум — залогировать ошибку и входные параметры.

   } catch (error: unknown) {
-    return [];
+    // TODO: заменить на централизованный логгер, если он есть в TRPCContext
+    console.error("getPopularByCategoryFn failed", { slug, limit, error });
+    return [];
   }

41-41: Дублирование default для limit (8) — оставьте один источник истины

Значение по умолчанию задано и в параметре функции, и в Zod-схеме. Достаточно в одном месте (рекомендуется в схеме).

-  limit = 8,
+  limit: number,

Also applies to: 152-153

packages/web-api/src/router/brands/get-featured.ts (1)

16-16: Незначительное: сортировка по isFeatured избыточна

where уже фильтрует isFeatured = true, сортировка по нему не влияет на порядок.

Примените дифф:

-      orderBy: [desc(brands.isFeatured), brands.name],
+      orderBy: [brands.name],
packages/web-api/src/router/checkout/validate-draft.ts (1)

10-16: Рассмотрите .output-схему для стабильности контракта

Сейчас нет .output — добавление её с zod-схемой ответа повысит предсказуемость контракта и защитит от регрессий.

Могу предложить zod-схему для формы:
{ success: boolean; data: { isValid: boolean; data: unknown; errors: ZodIssue[] | null; recommendations: string[] } | null; error: string | null }.

packages/web-api/src/lib/draft-utils.ts (1)

82-118: Опционально: типизируйте ошибки Zod и группы

Избавьтесь от any в groupValidationErrors/hasCriticalErrors для лучшей читаемости и автодополнения.

Пример:

import type { ZodIssue } from "zod";

export function groupValidationErrors(errors: ZodIssue[] | null) {
  if (!errors) return {};
  const groups: Record<"contact"|"shipping"|"payment"|"general", string[]> = {
    contact: [], shipping: [], payment: [], general: []
  };
  for (const error of errors) {
    const field = String(error.path[0] ?? "general");
    // ...
  }
  return groups;
}

export function hasCriticalErrors(errors: ZodIssue[] | null): boolean {
  if (!errors) return false;
  const criticalFields = ["email", "phone", "address", "city"];
  return errors.some(e => criticalFields.includes(String(e.path[0])));
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 55127e8 and 59ec3c7.

📒 Files selected for processing (9)
  • packages/web-api/src/lib/draft-utils.ts (2 hunks)
  • packages/web-api/src/router/account/orders/get-order-detail.ts (1 hunks)
  • packages/web-api/src/router/account/orders/get-orders.ts (3 hunks)
  • packages/web-api/src/router/brands/get-by-id.ts (1 hunks)
  • packages/web-api/src/router/brands/get-featured.ts (2 hunks)
  • packages/web-api/src/router/categories/get-all.ts (2 hunks)
  • packages/web-api/src/router/category/get-by-slug.ts (1 hunks)
  • packages/web-api/src/router/checkout/validate-draft.ts (2 hunks)
  • packages/web-api/src/router/products/get-popular-by-category.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

**/*.{ts,tsx}: Use strict typing in TypeScript and avoid any.
Use environment variables or @t3-oss/env-core for experimental feature flags.

Files:

  • packages/web-api/src/router/categories/get-all.ts
  • packages/web-api/src/router/account/orders/get-order-detail.ts
  • packages/web-api/src/lib/draft-utils.ts
  • packages/web-api/src/router/brands/get-by-id.ts
  • packages/web-api/src/router/account/orders/get-orders.ts
  • packages/web-api/src/router/category/get-by-slug.ts
  • packages/web-api/src/router/products/get-popular-by-category.ts
  • packages/web-api/src/router/checkout/validate-draft.ts
  • packages/web-api/src/router/brands/get-featured.ts
{packages/web-api/**,packages/api/**,packages/web-validators/**,packages/validators/**}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

Use the same Zod schemas on both frontend and backend. Import schemas from /packages/web-validators (for web) and /packages/validators (for app) to guarantee unified validation and type safety.

Files:

  • packages/web-api/src/router/categories/get-all.ts
  • packages/web-api/src/router/account/orders/get-order-detail.ts
  • packages/web-api/src/lib/draft-utils.ts
  • packages/web-api/src/router/brands/get-by-id.ts
  • packages/web-api/src/router/account/orders/get-orders.ts
  • packages/web-api/src/router/category/get-by-slug.ts
  • packages/web-api/src/router/products/get-popular-by-category.ts
  • packages/web-api/src/router/checkout/validate-draft.ts
  • packages/web-api/src/router/brands/get-featured.ts
{packages/web-api/**,packages/api/**}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

The server must prepare and return clear, frontend-ready data structures. All necessary transformations and formatting should be performed on the server, so that the frontend receives data in the exact shape it needs.

Files:

  • packages/web-api/src/router/categories/get-all.ts
  • packages/web-api/src/router/account/orders/get-order-detail.ts
  • packages/web-api/src/lib/draft-utils.ts
  • packages/web-api/src/router/brands/get-by-id.ts
  • packages/web-api/src/router/account/orders/get-orders.ts
  • packages/web-api/src/router/category/get-by-slug.ts
  • packages/web-api/src/router/products/get-popular-by-category.ts
  • packages/web-api/src/router/checkout/validate-draft.ts
  • packages/web-api/src/router/brands/get-featured.ts
{packages/web-api/**,packages/api/**,packages/lib/**}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

Keep business logic decoupled in /packages/web-api, /packages/api, or /packages/lib.

Files:

  • packages/web-api/src/router/categories/get-all.ts
  • packages/web-api/src/router/account/orders/get-order-detail.ts
  • packages/web-api/src/lib/draft-utils.ts
  • packages/web-api/src/router/brands/get-by-id.ts
  • packages/web-api/src/router/account/orders/get-orders.ts
  • packages/web-api/src/router/category/get-by-slug.ts
  • packages/web-api/src/router/products/get-popular-by-category.ts
  • packages/web-api/src/router/checkout/validate-draft.ts
  • packages/web-api/src/router/brands/get-featured.ts
**/*.ts

📄 CodeRabbit Inference Engine (.windsurfrules)

Use Zod for validation.

Files:

  • packages/web-api/src/router/categories/get-all.ts
  • packages/web-api/src/router/account/orders/get-order-detail.ts
  • packages/web-api/src/lib/draft-utils.ts
  • packages/web-api/src/router/brands/get-by-id.ts
  • packages/web-api/src/router/account/orders/get-orders.ts
  • packages/web-api/src/router/category/get-by-slug.ts
  • packages/web-api/src/router/products/get-popular-by-category.ts
  • packages/web-api/src/router/checkout/validate-draft.ts
  • packages/web-api/src/router/brands/get-featured.ts

⚙️ CodeRabbit Configuration File

**/*.ts: ОСНОВНЫЕ ПРИНЦИПЫ РАЗРАБОТКИ:

  1. Именование и стиль кода:

    • Имена файлов обязательно в kebab-case (например: my-component.ts, user-profile.tsx)
    • Функции в camelCase, компоненты в PascalCase, константы в UPPER_SNAKE_CASE
    • Имена переменных и функций должны четко отражать их назначение и реализацию
    • Избегайте сокращений и аббревиатур в именах (кроме общепринятых)
    • Поддерживайте консистентность в стиле кода (отступы, пробелы, переносы строк)
    • Следуйте принципам чистого кода (DRY, SOLID, KISS)
    • Используйте осмысленные имена для булевых переменных (isLoading, hasPermission, canEdit)
  2. TypeScript и типизация:

    • Используйте строгую типизацию и избегайте any, unknown и {} где это возможно
    • Правильно применяйте дженерики, интерфейсы и типы
    • Обеспечивайте корректные определения типов для параметров функций и возвращаемых значений
    • Предпочитайте union и intersection типы сложным условным конструкциям
    • Используйте утилитарные типы TypeScript (Pick, Omit, Partial, Required)
    • Создавайте переиспользуемые типы и интерфейсы
    • Избегайте дублирования типов, выносите общие типы в отдельные файлы
  3. Соблюдение правил линтера:

    • Следуйте правилам ESLint для React и TypeScript
    • Соблюдайте рекомендации Biome по форматированию и анализу кода
    • Убедитесь, что код не содержит предупреждений или ошибок линтера
    • Используйте автоматическое форматирование кода
  4. Алгоритмы и производительность:

    • Пишите понятные и читаемые алгоритмы, которые могут понять другие разработчики
    • Оптимизируйте сложность алгоритмов (временную и пространственную)
    • Избегайте чрезмерной вложенности и сложности
    • Правильно обрабатывайте граничные случаи и ошибки
    • Используйте подходящие структуры данных для задач
    • Применяйте мемоизацию где это уместно
  5. Безопасность и стабильность:

    • Реализуйте правильную валидацию входных данных и защиту от инъекций
    • Корректно обрабатывайте ошибки и исключения
    • Обеспечивайте...

Files:

  • packages/web-api/src/router/categories/get-all.ts
  • packages/web-api/src/router/account/orders/get-order-detail.ts
  • packages/web-api/src/lib/draft-utils.ts
  • packages/web-api/src/router/brands/get-by-id.ts
  • packages/web-api/src/router/account/orders/get-orders.ts
  • packages/web-api/src/router/category/get-by-slug.ts
  • packages/web-api/src/router/products/get-popular-by-category.ts
  • packages/web-api/src/router/checkout/validate-draft.ts
  • packages/web-api/src/router/brands/get-featured.ts
🧠 Learnings (5)
📚 Learning: 2025-08-01T07:58:07.363Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/web-api.mdc:0-0
Timestamp: 2025-08-01T07:58:07.363Z
Learning: Applies to apps/web/**/*.tsx : Import Drizzle ORM functions like eq, sql from qco/db, e.g., import { eq, sql } from "qco/db";

Applied to files:

  • packages/web-api/src/router/categories/get-all.ts
📚 Learning: 2025-08-01T07:57:06.890Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/api.mdc:0-0
Timestamp: 2025-08-01T07:57:06.890Z
Learning: Applies to packages/api/**/*.ts : Import functions like eq, sql from qco/db, e.g., import { eq, sql } from "qco/db";

Applied to files:

  • packages/web-api/src/router/categories/get-all.ts
📚 Learning: 2025-08-01T07:58:07.363Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/web-api.mdc:0-0
Timestamp: 2025-08-01T07:58:07.363Z
Learning: Applies to apps/web/**/*.tsx : Import table schemas from qco/db/schema, e.g., import { Category, ProductCategory } from "qco/db/schema";

Applied to files:

  • packages/web-api/src/router/categories/get-all.ts
📚 Learning: 2025-08-01T07:57:06.890Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/api.mdc:0-0
Timestamp: 2025-08-01T07:57:06.890Z
Learning: Applies to packages/api/**/*.ts : Import schemas from qco/db/schema, e.g., import { Category, ProductCategory } from "qco/db/schema";

Applied to files:

  • packages/web-api/src/router/categories/get-all.ts
📚 Learning: 2025-08-01T07:58:22.450Z
Learnt from: CR
PR: qcohq/qco#0
File: .windsurfrules:0-0
Timestamp: 2025-08-01T07:58:22.450Z
Learning: Applies to packages/db/**/*.{ts,js} : Database schemas are defined in `qco/db`.

Applied to files:

  • packages/web-api/src/router/categories/get-all.ts
🧬 Code Graph Analysis (7)
packages/web-api/src/router/categories/get-all.ts (1)
packages/web-api/src/router/category/list.ts (1)
  • categoriesQuery (8-39)
packages/web-api/src/router/account/orders/get-order-detail.ts (2)
packages/api/src/router/orders/by-id.ts (2)
  • combination (38-42)
  • getVariantOptions (20-48)
packages/api/src/router/orders/list.ts (2)
  • combination (38-42)
  • getVariantOptions (19-49)
packages/web-api/src/lib/draft-utils.ts (1)
packages/web-validators/src/checkout.ts (2)
  • CheckoutDraftPartialValues (247-247)
  • validateDraftData (206-221)
packages/web-api/src/router/brands/get-by-id.ts (2)
apps/web/src/components/category-hero-section.tsx (2)
  • Banner (24-37)
  • BannerFile (11-22)
apps/web/src/components/hero-section.tsx (2)
  • Banner (24-42)
  • BannerFile (11-22)
packages/web-api/src/router/account/orders/get-orders.ts (3)
packages/api/src/router/orders/list.ts (2)
  • getVariantOptions (19-49)
  • acc (136-139)
packages/api/src/router/orders/by-id.ts (1)
  • getVariantOptions (20-48)
packages/web-api/src/router/products/get-by-slug.ts (1)
  • variant (259-298)
packages/web-api/src/router/category/get-by-slug.ts (1)
packages/web-api/src/router/category/list.ts (1)
  • category (27-38)
packages/web-api/src/router/checkout/validate-draft.ts (2)
packages/web-validators/src/checkout.ts (1)
  • validateDraftData (206-221)
apps/web/src/features/checkout/components/checkout-page-client.tsx (2)
  • value (118-123)
  • draftError (128-134)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test API Package
🔇 Additional comments (5)
packages/web-api/src/router/categories/get-all.ts (2)

1-1: Импорт isNull из @qco/db — корректно

Переход на явный импорт isNull согласуется с практикой Drizzle и с использованием ниже по коду.


16-19: Правильная замена eq(..., null) на isNull(...)

Проверка корневых категорий через isNull(categories.parentId) — корректный и типобезопасный способ для Drizzle. Логика фильтрации осталась неизменной и понятной.

packages/web-api/src/router/category/get-by-slug.ts (2)

30-41: Явное формирование ответа — читаемо и типобезопасно

Отказ от spread в пользу явного маппинга полей повышает предсказуемость структуры ответа и упрощает сопровождение/валидацию схемой Zod.


39-40: TRPC уже настроен с SuperJSON — проверьте Zod-схему полей createdAt/updatedAt

TRPC-конфиг содержит transformer: superjson, поэтому даты сериализуются как объекты Date. Однако в текущем репозитории не найдено export const categorySchemaV2 в packages/web-validators, где должны быть описаны типы полей ответа.

Пожалуйста, проверьте и скорректируйте:

  • Найдите фактическую Zod-схему, отвечающую за маршрут get-by-slugpackages/web-validators или аналогичной папке).
  • Убедитесь, что в этой схеме поля
    • createdAt и updatedAt описаны как z.date() (если клиент ожидает объект Date),
    • либо как z.string() (если ожидаются ISO-строки) и фронтенд к этому готов.
  • При необходимости обновите схему или фронтенд-обработку, чтобы типы совпадали.
packages/web-api/src/router/brands/get-by-id.ts (1)

59-61: Поле banner уже включено в brandDetailSchema
В файле packages/web-validators/src/brands.ts (строка 105) определено

banner: z.string().nullable(), // Дополнительное поле для фронтенда

Таким образом, возврат banner: null соответствует схеме.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🔭 Outside diff range comments (2)
packages/api/src/router/orders/create.ts (1)

47-70: Сделать создание заказа и позиций атомарным (транзакция) и вставлять позиции батчем.

Сейчас заказ создаётся отдельно, а позиции — в цикле вне транзакции. При сбое получите частично созданные данные. Кроме того, батч-вставка быстрее и чище.

Предлагаемый рефактор:

-    const [order] = await ctx.db.insert(orders).values(values).returning();
-
-    if (!order) {
-      throw new TRPCError({
-        code: "INTERNAL_SERVER_ERROR",
-        message: "Failed to create order",
-      });
-    }
-
-    // Create order items
-    for (const item of items) {
-      await ctx.db.insert(orderItems).values({
-        orderId: order.id,
-        productId: item.productId,
-        variantId: item.variantId,
-        quantity: item.quantity,
-        unitPrice: item.unitPrice,
-        totalPrice: item.totalPrice,
-        productName: item.productName,
-        productSku: item.productSku,
-        variantName: item.variantName,
-
-      });
-    }
-
-    return order;
+    const createdOrder = await ctx.db.transaction(async (tx) => {
+      const [order] = await tx.insert(orders).values(values).returning();
+      if (!order) {
+        throw new TRPCError({
+          code: "INTERNAL_SERVER_ERROR",
+          message: "Failed to create order",
+        });
+      }
+
+      if (items.length > 0) {
+        await tx.insert(orderItems).values(
+          items.map((item) => ({
+            orderId: order.id,
+            productId: item.productId,
+            variantId: item.variantId,
+            quantity: item.quantity,
+            unitPrice: item.unitPrice,
+            totalPrice: item.totalPrice,
+            productName: item.productName,
+            productSku: item.productSku,
+            variantName: item.variantName,
+          })),
+        );
+      }
+      return order;
+    });
+
+    return createdOrder;
packages/api/src/router/orders/list.ts (1)

20-20: Рекомендуется заменить any на более строгие типы.

Согласно руководящим принципам разработки, следует избегать использования any и применять строгую типизацию. Рекомендуется создать интерфейсы для типизации параметров.

Создайте типы для улучшения типобезопасности:

+interface OptionCombination {
+  option: { name: string; slug: string } | null;
+  optionValue: { displayName?: string; value: string } | null;
+}
+
+interface OrderItem {
+  productId: string;
+  variantId?: string;
+  productSku?: string;
+  variantName?: string;
+  // добавьте другие необходимые поля
+}
+
+interface Order {
+  items?: OrderItem[];
+  customer?: any; // определите интерфейс для customer
+  // добавьте другие поля заказа
+}

 async function getVariantOptions(
-  ctx: any,
+  ctx: { db: any }, // или используйте правильный тип контекста
   variantId: string
 ): Promise<Array<{ name: string; value: string; slug: string }>> {
   // ...
   const variantOptions = optionCombinations
-    .filter((combination: any) => combination.option && combination.optionValue)
-    .map((combination: any) => ({
+    .filter((combination: OptionCombination) => combination.option && combination.optionValue)
+    .map((combination: OptionCombination) => ({
       // ...

-return await Promise.all(data.map(async (order: any) => ({
+return await Promise.all(data.map(async (order: Order) => ({
   // ...
-  items: await Promise.all(order.items?.map(async (item: any) => {
+  items: await Promise.all(order.items?.map(async (item: OrderItem) => {

Also applies to: 129-129, 143-143, 145-145

♻️ Duplicate comments (2)
packages/config/package.json (2)

6-13: Экспорт на собранные артефакты из dist — отлично, это исправляет прежнюю проблему

Теперь потребители не будут тянуть .ts-файлы; корректные exports, main и types указывают на dist.


24-27: Пины версий зависимостей выглядят уместно

Для критичных пакетов (@t3-oss/env-core, zod) фиксированные версии — разумно. Совместимость env-core 0.13.x с Zod v4 подтверждена в предыдущем обсуждении.

🧹 Nitpick comments (5)
packages/config/package.json (3)

4-4: Подтвердите private:true; если пакет планируется к публикации — снимите private и задайте publishConfig

С текущим "private": true пакет не будет опубликован, что ок для внутреннего workspace-зависимого пакета. Подтвердите намерение. Если публикация нужна — примените:

-  "private": true,
+  "private": false,
+  "publishConfig": {
+    "access": "public"
+  },

1-5: Добавьте поле license (для private пакета — UNLICENSED)

Явная лицензия улучшает соответствие инструментам и требованиям комплаенса.

 {
   "name": "@qco/config",
   "version": "0.0.0",
   "private": true,
+  "license": "UNLICENSED",
   "type": "module",

28-36: Зафиксируйте поддерживаемую версию Node через engines

Это поможет избежать конфликтов окружений в монорепозитории и CI.

   "devDependencies": {
     "@qco/eslint-config": "workspace:*",
     "@qco/prettier-config": "workspace:*",
     "@qco/tsconfig": "workspace:*",
     "eslint": "9.32.0",
     "prettier": "3.6.2",
     "tsup": "8.5.0",
     "typescript": "5.9.2"
   },
+  "engines": {
+    "node": ">=18"
+  },
   "prettier": "@qco/prettier-config"
packages/api/src/router/orders/get-by-customer.ts (1)

28-31: Уточнить приведение типа статуса к типу столбца БД (и опционально валидировать значение).

Привязка к объекту OrderStatus в приведения типа избыточна и хрупка. Надёжнее сузить тип до фактического типа столбца: typeof orders.$inferSelect["status"].

Прямой фикс (без дополнительной валидации):

-            whereConditions.push(eq(orders.status, status as typeof OrderStatus[keyof typeof OrderStatus]));
+            // Узкое приведение к типу столбца БД, без зависимости от объекта OrderStatus
+            whereConditions.push(eq(orders.status, status as typeof orders.$inferSelect["status"]));

Дополнительно (вне этого блока) можно валидировать входной статус и бросать BAD_REQUEST:

const allowedStatuses = Object.values(OrderStatus) as Array<typeof orders.$inferSelect["status"]>;
if (!allowedStatuses.includes(status as any)) {
  throw new TRPCError({ code: "BAD_REQUEST", message: "Invalid status" });
}
packages/api/src/router/orders/create.ts (1)

4-4: Можно упростить типизацию и избавиться от лишних импортов enum’ов.

Сейчас типизация полей основана на импортированных DB-enum’ах. Чтобы убрать лишние зависимости и дублирование, можно приводить к типам столбцов через orders.$inferInsert[...] и удалить импорты PaymentMethod/PaymentStatus/ShippingMethod.

Изменить импорт:

-import { orders, orderItems, customers, PaymentMethod, PaymentStatus, ShippingMethod } from "@qco/db/schema";
+import { orders, orderItems, customers } from "@qco/db/schema";

И приведения типов в values:

-      paymentMethod: orderData.paymentMethod as typeof PaymentMethod[keyof typeof PaymentMethod] | undefined,
-      shippingMethod: orderData.shippingMethod as typeof ShippingMethod[keyof typeof ShippingMethod] | undefined,
+      paymentMethod: orderData.paymentMethod as typeof orders.$inferInsert["paymentMethod"] | undefined,
+      shippingMethod: orderData.shippingMethod as typeof orders.$inferInsert["shippingMethod"] | undefined,
-      paymentStatus: orderData.paymentStatus as typeof PaymentStatus[keyof typeof PaymentStatus] | undefined,
+      paymentStatus: orderData.paymentStatus as typeof orders.$inferInsert["paymentStatus"] | undefined,
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59ec3c7 and 4cb87fc.

📒 Files selected for processing (6)
  • packages/api/src/router/orders/create.ts (2 hunks)
  • packages/api/src/router/orders/get-by-customer.ts (3 hunks)
  • packages/api/src/router/orders/list.ts (2 hunks)
  • packages/api/src/router/product-attribute-values/upsert.ts (2 hunks)
  • packages/config/package.json (1 hunks)
  • packages/web-api/src/router/products/get-new.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/web-api/src/router/products/get-new.ts
🧰 Additional context used
📓 Path-based instructions (10)
packages/api/**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/api.mdc)

packages/api/**/*.ts: Import schemas from @qco/db/schema, e.g., import { Category, ProductCategory } from "@qco/db/schema";
Import functions like eq, sql from @qco/db, e.g., import { eq, sql } from "@qco/db";
Use Zod for input and output validation, importing from @qco/validators.
Use TRPCError for consistent error handling across all routes.

Files:

  • packages/api/src/router/product-attribute-values/upsert.ts
  • packages/api/src/router/orders/list.ts
  • packages/api/src/router/orders/create.ts
  • packages/api/src/router/orders/get-by-customer.ts
packages/api/src/router/**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/api.mdc)

Use ctx for database access in tRPC routes to ensure consistent data handling.

Files:

  • packages/api/src/router/product-attribute-values/upsert.ts
  • packages/api/src/router/orders/list.ts
  • packages/api/src/router/orders/create.ts
  • packages/api/src/router/orders/get-by-customer.ts
packages/api/src/router/*/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/api.mdc)

Each tRPC route should be in a separate file within a group folder under packages/api/src/router/{group-name}/, named {action}.ts (e.g., create.ts, list.ts, update.ts, delete.ts).

Files:

  • packages/api/src/router/product-attribute-values/upsert.ts
  • packages/api/src/router/orders/list.ts
  • packages/api/src/router/orders/create.ts
  • packages/api/src/router/orders/get-by-customer.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

**/*.{ts,tsx}: Use strict typing in TypeScript and avoid any.
Use environment variables or @t3-oss/env-core for experimental feature flags.

Files:

  • packages/api/src/router/product-attribute-values/upsert.ts
  • packages/api/src/router/orders/list.ts
  • packages/api/src/router/orders/create.ts
  • packages/api/src/router/orders/get-by-customer.ts
{packages/web-api/**,packages/api/**,packages/web-validators/**,packages/validators/**}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

Use the same Zod schemas on both frontend and backend. Import schemas from /packages/web-validators (for web) and /packages/validators (for app) to guarantee unified validation and type safety.

Files:

  • packages/api/src/router/product-attribute-values/upsert.ts
  • packages/api/src/router/orders/list.ts
  • packages/api/src/router/orders/create.ts
  • packages/api/src/router/orders/get-by-customer.ts
{packages/web-api/**,packages/api/**}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

The server must prepare and return clear, frontend-ready data structures. All necessary transformations and formatting should be performed on the server, so that the frontend receives data in the exact shape it needs.

Files:

  • packages/api/src/router/product-attribute-values/upsert.ts
  • packages/api/src/router/orders/list.ts
  • packages/api/src/router/orders/create.ts
  • packages/api/src/router/orders/get-by-customer.ts
{packages/web-api/**,packages/api/**,packages/lib/**}

📄 CodeRabbit Inference Engine (.cursor/rules/code.mdc)

Keep business logic decoupled in /packages/web-api, /packages/api, or /packages/lib.

Files:

  • packages/api/src/router/product-attribute-values/upsert.ts
  • packages/api/src/router/orders/list.ts
  • packages/api/src/router/orders/create.ts
  • packages/api/src/router/orders/get-by-customer.ts
**/*.ts

📄 CodeRabbit Inference Engine (.windsurfrules)

Use Zod for validation.

Files:

  • packages/api/src/router/product-attribute-values/upsert.ts
  • packages/api/src/router/orders/list.ts
  • packages/api/src/router/orders/create.ts
  • packages/api/src/router/orders/get-by-customer.ts

⚙️ CodeRabbit Configuration File

**/*.ts: ОСНОВНЫЕ ПРИНЦИПЫ РАЗРАБОТКИ:

  1. Именование и стиль кода:

    • Имена файлов обязательно в kebab-case (например: my-component.ts, user-profile.tsx)
    • Функции в camelCase, компоненты в PascalCase, константы в UPPER_SNAKE_CASE
    • Имена переменных и функций должны четко отражать их назначение и реализацию
    • Избегайте сокращений и аббревиатур в именах (кроме общепринятых)
    • Поддерживайте консистентность в стиле кода (отступы, пробелы, переносы строк)
    • Следуйте принципам чистого кода (DRY, SOLID, KISS)
    • Используйте осмысленные имена для булевых переменных (isLoading, hasPermission, canEdit)
  2. TypeScript и типизация:

    • Используйте строгую типизацию и избегайте any, unknown и {} где это возможно
    • Правильно применяйте дженерики, интерфейсы и типы
    • Обеспечивайте корректные определения типов для параметров функций и возвращаемых значений
    • Предпочитайте union и intersection типы сложным условным конструкциям
    • Используйте утилитарные типы TypeScript (Pick, Omit, Partial, Required)
    • Создавайте переиспользуемые типы и интерфейсы
    • Избегайте дублирования типов, выносите общие типы в отдельные файлы
  3. Соблюдение правил линтера:

    • Следуйте правилам ESLint для React и TypeScript
    • Соблюдайте рекомендации Biome по форматированию и анализу кода
    • Убедитесь, что код не содержит предупреждений или ошибок линтера
    • Используйте автоматическое форматирование кода
  4. Алгоритмы и производительность:

    • Пишите понятные и читаемые алгоритмы, которые могут понять другие разработчики
    • Оптимизируйте сложность алгоритмов (временную и пространственную)
    • Избегайте чрезмерной вложенности и сложности
    • Правильно обрабатывайте граничные случаи и ошибки
    • Используйте подходящие структуры данных для задач
    • Применяйте мемоизацию где это уместно
  5. Безопасность и стабильность:

    • Реализуйте правильную валидацию входных данных и защиту от инъекций
    • Корректно обрабатывайте ошибки и исключения
    • Обеспечивайте...

Files:

  • packages/api/src/router/product-attribute-values/upsert.ts
  • packages/api/src/router/orders/list.ts
  • packages/api/src/router/orders/create.ts
  • packages/api/src/router/orders/get-by-customer.ts
**/*.json

⚙️ CodeRabbit Configuration File

**/*.json: РАБОТА С JSON ФАЙЛАМИ:

  1. Структура и форматирование:

    • Используйте правильное форматирование JSON
    • Поддерживайте консистентную структуру
    • Используйте осмысленные ключи
    • Избегайте дублирования данных
  2. Валидация:

    • Убедитесь в корректности JSON синтаксиса
    • Проверяйте типы данных
    • Валидируйте обязательные поля
  3. Безопасность:

    • Не включайте конфиденциальные данные
    • Используйте переменные окружения для секретов
    • Валидируйте входящие JSON данные

Files:

  • packages/config/package.json
**/package.json

⚙️ CodeRabbit Configuration File

**/package.json: УПРАВЛЕНИЕ ЗАВИСИМОСТЯМИ:

  1. Версионирование:

    • Используйте семантическое версионирование
    • Избегайте уязвимых версий пакетов
    • Регулярно обновляйте зависимости
  2. Структура:

    • Правильно организуйте scripts
    • Разделяйте dependencies и devDependencies
    • Используйте точные версии для критических пакетов
  3. Безопасность:

    • Проверяйте пакеты на уязвимости
    • Используйте только проверенные пакеты
    • Регулярно аудируйте зависимости

Files:

  • packages/config/package.json
🧠 Learnings (9)
📚 Learning: 2025-08-01T07:58:07.363Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/web-api.mdc:0-0
Timestamp: 2025-08-01T07:58:07.363Z
Learning: Applies to apps/web/**/*.tsx : Import Drizzle ORM functions like eq, sql from qco/db, e.g., import { eq, sql } from "qco/db";

Applied to files:

  • packages/api/src/router/orders/create.ts
  • packages/api/src/router/orders/get-by-customer.ts
📚 Learning: 2025-08-01T07:57:06.890Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/api.mdc:0-0
Timestamp: 2025-08-01T07:57:06.890Z
Learning: Applies to packages/api/**/*.ts : Import functions like eq, sql from qco/db, e.g., import { eq, sql } from "qco/db";

Applied to files:

  • packages/api/src/router/orders/create.ts
  • packages/api/src/router/orders/get-by-customer.ts
📚 Learning: 2025-08-01T07:58:22.450Z
Learnt from: CR
PR: qcohq/qco#0
File: .windsurfrules:0-0
Timestamp: 2025-08-01T07:58:22.450Z
Learning: Applies to packages/db/**/*.{ts,js} : Database schemas are defined in `qco/db`.

Applied to files:

  • packages/api/src/router/orders/create.ts
  • packages/api/src/router/orders/get-by-customer.ts
  • packages/config/package.json
📚 Learning: 2025-08-01T07:57:06.890Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/api.mdc:0-0
Timestamp: 2025-08-01T07:57:06.890Z
Learning: Applies to packages/api/**/*.ts : Use TRPCError for consistent error handling across all routes.

Applied to files:

  • packages/api/src/router/orders/create.ts
  • packages/api/src/router/orders/get-by-customer.ts
📚 Learning: 2025-08-01T07:58:22.450Z
Learnt from: CR
PR: qcohq/qco#0
File: .windsurfrules:0-0
Timestamp: 2025-08-01T07:58:22.450Z
Learning: Applies to backend/**/*.{ts,js} : Use Drizzle ORM for database operations.

Applied to files:

  • packages/api/src/router/orders/create.ts
  • packages/api/src/router/orders/get-by-customer.ts
📚 Learning: 2025-08-01T07:57:06.890Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/api.mdc:0-0
Timestamp: 2025-08-01T07:57:06.890Z
Learning: Applies to packages/api/**/*.ts : Import schemas from qco/db/schema, e.g., import { Category, ProductCategory } from "qco/db/schema";

Applied to files:

  • packages/api/src/router/orders/create.ts
📚 Learning: 2025-08-01T07:58:22.450Z
Learnt from: CR
PR: qcohq/qco#0
File: .windsurfrules:0-0
Timestamp: 2025-08-01T07:58:22.450Z
Learning: Applies to backend/**/*.{ts,js} : Use Drizzle for database access.

Applied to files:

  • packages/api/src/router/orders/create.ts
📚 Learning: 2025-08-01T07:58:07.363Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/web-api.mdc:0-0
Timestamp: 2025-08-01T07:58:07.363Z
Learning: Applies to apps/web/**/*.tsx : Use ctx for database access in tRPC routes to ensure consistent data handling

Applied to files:

  • packages/api/src/router/orders/get-by-customer.ts
📚 Learning: 2025-08-01T07:57:06.890Z
Learnt from: CR
PR: qcohq/qco#0
File: .cursor/rules/api.mdc:0-0
Timestamp: 2025-08-01T07:57:06.890Z
Learning: Applies to packages/api/**/*.ts : Use Zod for input and output validation, importing from qco/validators.

Applied to files:

  • packages/config/package.json
🧬 Code Graph Analysis (3)
packages/api/src/router/orders/list.ts (2)
packages/web-api/src/router/account/orders/get-order-detail.ts (2)
  • getVariantOptions (20-50)
  • acc (91-94)
packages/api/src/router/orders/by-id.ts (1)
  • combination (38-42)
packages/api/src/router/orders/create.ts (2)
packages/web-validators/src/orders.ts (3)
  • PaymentMethod (179-179)
  • ShippingMethod (178-178)
  • PaymentStatus (183-183)
packages/web-api/src/lib/orders/order-helpers.ts (2)
  • createPaymentMethod (543-550)
  • tx (235-243)
packages/api/src/router/orders/get-by-customer.ts (3)
packages/api/src/router/orders/update-status.ts (1)
  • input (10-150)
packages/api/src/router/orders/update-delivery-status.ts (1)
  • input (9-111)
packages/web-api/src/router/orders/get-orders.ts (1)
  • input (22-154)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test API Package
🔇 Additional comments (10)
packages/config/package.json (1)

14-15: files/sideEffects настроены корректно

"files": ["dist"] ограничивает публикуемый набор, а "sideEffects": false улучшает tree-shaking.

packages/api/src/router/orders/get-by-customer.ts (4)

2-2: Импорт eq/and из @qco/db — соответствует правилам репозитория.

Это корректная миграция с drizzle-orm на пакетную реэкспорт-обёртку.


4-4: Импорт схем из @qco/db/schema — ок.

Соответствует гайдлайнам: схемы должны приходить из @qco/db/schema.


53-53: Возможный breaking change: totalAmount сериализуется строкой. Проверьте согласованность API.

В других эндпоинтах суммы отдаются числом (см. packages/api/src/router/account/orders/get-orders.ts — Number(order.totalAmount)). Если фронт ожидает число, текущее String(...) поломает типы/схемы.

Предлагаемый фикс (если договорённость — числа):

-                totalAmount: String(order.totalAmount),
+                totalAmount: Number(order.totalAmount),

Пожалуйста, подтвердите:

  • что getOrdersByCustomerOutputSchema ожидает число (а не строку),
  • и что типы согласованы с web-валидаторами.

56-56: Нормализованная сериализация cancelledAt — ок.

Явная проверка вместо ?. || null делает намерение читаемым и не меняет поведение.

packages/api/src/router/orders/create.ts (3)

2-2: Импорт eq из @qco/db — ок.

Соответствует принятым правилам импорта ORM-хелперов.


32-33: Проверьте соответствие значения статуса enum’у в БД.

Хардкод "pending" может не совпадать с реальным набором значений (например, "PENDING"). Лучше использовать enum из схемы или сузить литерал до типа столбца.

Варианты:

  • Если в @qco/db/schema есть константа: OrderStatus.PENDING
-      status: "pending",
+      status: OrderStatus.PENDING,
  • Минимальный безопасный вариант (без импорта enum’а):
-      status: "pending",
+      status: "pending" as typeof orders.$inferInsert["status"],

Пожалуйста, подтвердите фактическое значение статуса и обновите при необходимости.


44-45: paymentStatus теперь не дефолтится. Убедитесь, что это ожидаемо.

Ранее значение по умолчанию могло быть "PENDING". Если фронт не присылает paymentStatus, проверьте:

  • есть ли дефолт на уровне БД, или
  • нужно ли здесь выставлять начальный статус явно.

Если нужен дефолт в коде:

-      paymentStatus: orderData.paymentStatus as typeof orders.$inferInsert["paymentStatus"] | undefined,
+      paymentStatus: (orderData.paymentStatus ?? "PENDING") as typeof orders.$inferInsert["paymentStatus"],

Иначе — задокументируйте, что поле может быть undefined, и синхронизируйте схему валидатора.

packages/api/src/router/orders/list.ts (2)

37-42: Добавлена явная типизация параметров для улучшения читаемости кода.

Хорошее изменение для обеспечения согласованности с другими частями кодовой базы. Аналогичная типизация применена в packages/web-api/src/router/account/orders/get-order-detail.ts и packages/web-api/src/router/account/orders/get-orders.ts.


143-145: Добавлена явная типизация для callback функций в map.

Изменения соответствуют паттерну явной типизации inline callback'ов, применяемому в других маршрутах. Это улучшает читаемость и согласованность кода.

kodermax and others added 12 commits August 13, 2025 09:06
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@kodermax kodermax merged commit 7d659aa into main Aug 13, 2025
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant