Skip to content

feat: legal and agreement create actions#15

Merged
hmbanan666 merged 2 commits into
mainfrom
new-actions
Jul 24, 2025
Merged

feat: legal and agreement create actions#15
hmbanan666 merged 2 commits into
mainfrom
new-actions

Conversation

@hmbanan666
Copy link
Copy Markdown
Collaborator

@hmbanan666 hmbanan666 commented Jul 23, 2025

Summary by CodeRabbit

  • New Features

    • Added cards for displaying partner agreements and legal entities, with detailed information and progress indicators.
    • Introduced forms and modals for creating partner agreements and legal entities, including validation, localized date pickers, and user feedback via notifications.
    • Added API endpoints to support creation of partner agreements and legal entities.
    • Extended Russian localization with new messages for partner-related actions.
  • Enhancements

    • Improved partner page layout and replaced inline details with new card components.
    • Added smooth fade-in effect on partner avatar overlays.
  • Bug Fixes

    • None.
  • Documentation

    • None.
  • Chores

    • Added validation schemas and types for partner entities to streamline form validation and API integration.

@hmbanan666 hmbanan666 self-assigned this Jul 23, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jul 23, 2025

Walkthrough

This change introduces new Vue components for managing and displaying partner legal entities and agreements, including creation forms and modal dialogs. It adds backend API endpoints and repository methods for creating these entities, integrates validation schemas, updates localization for new messages, and refactors the partner page to use the new components, removing inline logic.

Changes

File(s) Change Summary
app/components/PartnerAgreementCard.vue, PartnerLegalEntityCard.vue Added new Vue components for displaying partner agreements and legal entities, with conditional rendering and modal triggers.
app/components/form/CreatePartnerAgreement.vue, CreatePartnerLegalEntity.vue Added form components for creating partner agreements and legal entities, including validation, API calls, and events.
app/components/modal/CreatePartnerAgreement.vue, CreatePartnerLegalEntity.vue Added modal components to wrap creation forms for agreements and legal entities, managing overlay/modal state.
app/pages/partner/[id]/index.vue Refactored partner page: replaced inline entity/agreement markup with new card components, updated layout, removed local logic.
app/components/PartnerCard.vue Updated overlay styling for avatar image: added fade-in effect on hover.
i18n/locales/ru-RU.json Added Russian translations for new entity/agreement actions and comments.
server/api/partner/id/[partnerId]/agreement.post.ts, legal.post.ts Added POST API endpoints for creating partner agreements and legal entities, including validation and error handling.
shared/services/partner.ts Introduced validation schemas and TypeScript types for partner legal entities and agreements using arktype.
packages/database/src/repository/partner.ts Added repository methods for creating partner legal entities and agreements in the database.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant PartnerPage
    participant PartnerLegalEntityCard
    participant CreatePartnerLegalEntityModal
    participant CreatePartnerLegalEntityForm
    participant API
    participant PartnerStore

    User->>PartnerPage: Visits partner page
    PartnerPage->>PartnerLegalEntityCard: Renders card
    User->>PartnerLegalEntityCard: Clicks "Add Legal Entity"
    PartnerLegalEntityCard->>CreatePartnerLegalEntityModal: Opens modal
    CreatePartnerLegalEntityModal->>CreatePartnerLegalEntityForm: Renders form
    User->>CreatePartnerLegalEntityForm: Submits form
    CreatePartnerLegalEntityForm->>API: POST /api/partner/id/{partnerId}/legal
    API-->>CreatePartnerLegalEntityForm: Returns created entity
    CreatePartnerLegalEntityForm->>PartnerStore: Updates store
    CreatePartnerLegalEntityForm->>CreatePartnerLegalEntityModal: Emits success
    CreatePartnerLegalEntityModal->>PartnerLegalEntityCard: Closes modal, triggers UI update
Loading
sequenceDiagram
    participant User
    participant PartnerPage
    participant PartnerAgreementCard
    participant CreatePartnerAgreementModal
    participant CreatePartnerAgreementForm
    participant API
    participant PartnerStore

    User->>PartnerPage: Visits partner page
    PartnerPage->>PartnerAgreementCard: Renders card
    User->>PartnerAgreementCard: Clicks "Add Agreement"
    PartnerAgreementCard->>CreatePartnerAgreementModal: Opens modal
    CreatePartnerAgreementModal->>CreatePartnerAgreementForm: Renders form
    User->>CreatePartnerAgreementForm: Submits form
    CreatePartnerAgreementForm->>API: POST /api/partner/id/{partnerId}/agreement
    API-->>CreatePartnerAgreementForm: Returns created agreement
    CreatePartnerAgreementForm->>PartnerStore: Updates store
    CreatePartnerAgreementForm->>CreatePartnerAgreementModal: Emits success
    CreatePartnerAgreementModal->>PartnerAgreementCard: Closes modal, triggers UI update
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~90 minutes

Possibly related PRs

  • sushi-love/roll-stack#14: Introduced a PartnerAgreementCard.vue component encapsulating agreement progress and display logic, similar to this PR's component extraction and refactoring.

Poem

In the garden of code, new cards now bloom,
Partners and pacts find their digital room.
Modals pop open with a hop and a cheer,
Forms validate swiftly—no bugs to fear!
🐇✨
With overlays and toasts, we celebrate anew,
This patch brings fresh carrots for the review!

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a758ce and 0696ada.

📒 Files selected for processing (2)
  • apps/web-app/app/components/PartnerCard.vue (1 hunks)
  • apps/web-app/shared/services/partner.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web-app/app/components/PartnerCard.vue
  • apps/web-app/shared/services/partner.ts
⏰ 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: build
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch new-actions

🪧 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.
    • Explain this complex logic.
    • 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. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • 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
Copy Markdown

@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: 14

🧹 Nitpick comments (8)
apps/web-app/app/components/modal/CreatePartnerAgreement.vue (2)

5-6: Consider using undefined instead of empty strings for missing props.

Defaulting optional props to empty strings might cause issues in the form component. Consider passing undefined when props are not provided:

-        :partner-id="partnerId ?? ''"
-        :legal-entity-id="legalEntityId ?? ''"
+        :partner-id="partnerId"
+        :legal-entity-id="legalEntityId"

2-2: Consider using i18n for the modal title.

The hardcoded Russian title should ideally use internationalization for better maintainability:

-  <UModal title="Создание договора">
+  <UModal :title="$t('partner.agreement.create.title')">
apps/web-app/app/components/modal/CreatePartnerLegalEntity.vue (2)

5-5: Consider using undefined instead of empty string for missing prop.

Similar to the agreement modal, defaulting the optional prop to an empty string might cause issues:

-        :partner-id="partnerId ?? ''"
+        :partner-id="partnerId"

2-2: Consider using i18n for the modal title.

The hardcoded Russian title should use internationalization:

-  <UModal title="Создание юридического лица">
+  <UModal :title="$t('partner.legalEntity.create.title')">
apps/web-app/app/components/PartnerLegalEntityCard.vue (1)

20-25: Modal integration looks good, but consider i18n for the label.

The creation card implementation and modal integration follow the established patterns well. Consider using internationalization for the label:

-    label="Добавить юридическое лицо"
+    :label="$t('partner.legalEntity.add')"
apps/web-app/server/api/partner/id/[partnerId]/agreement.post.ts (1)

27-27: Improve error message specificity.

The error message "Unable to create" is too generic and doesn't provide enough context.

Apply this diff to make the error message more specific:

-        message: 'Unable to create',
+        message: 'Unable to create partner agreement',
apps/web-app/app/components/form/CreatePartnerAgreement.vue (2)

163-166: Improve error handling specificity.

The current error handling is generic. Consider providing more specific error messages based on the error type.

   } catch (error) {
     console.error(error)
-    actionToast.error(toastId)
+    const errorMessage = error instanceof Error ? error.message : t('error.unknown')
+    actionToast.error(toastId, errorMessage)
   }

149-167: Add client-side business rule validation.

Consider adding validation for business rules before form submission for better user experience:

  • End date should be after conclusion date
  • Royalty percentage should be reasonable (0-100%)
  • Monetary values should be positive
 async function onSubmit(event: FormSubmitEvent<CreatePartnerAgreement>) {
+  // Business rule validation
+  if (state.value.concludedAt && state.value.willEndAt) {
+    const concludedDate = new Date(state.value.concludedAt)
+    const endDate = new Date(state.value.willEndAt)
+    if (endDate <= concludedDate) {
+      actionToast.error(undefined, t('error.agreement.invalidDateRange'))
+      return
+    }
+  }
+
   const toastId = actionToast.start()
   emit('submitted')
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9766024 and 7a758ce.

📒 Files selected for processing (13)
  • apps/web-app/app/components/PartnerAgreementCard.vue (1 hunks)
  • apps/web-app/app/components/PartnerCard.vue (1 hunks)
  • apps/web-app/app/components/PartnerLegalEntityCard.vue (1 hunks)
  • apps/web-app/app/components/form/CreatePartnerAgreement.vue (1 hunks)
  • apps/web-app/app/components/form/CreatePartnerLegalEntity.vue (1 hunks)
  • apps/web-app/app/components/modal/CreatePartnerAgreement.vue (1 hunks)
  • apps/web-app/app/components/modal/CreatePartnerLegalEntity.vue (1 hunks)
  • apps/web-app/app/pages/partner/[id]/index.vue (2 hunks)
  • apps/web-app/i18n/locales/ru-RU.json (2 hunks)
  • apps/web-app/server/api/partner/id/[partnerId]/agreement.post.ts (1 hunks)
  • apps/web-app/server/api/partner/id/[partnerId]/legal.post.ts (1 hunks)
  • apps/web-app/shared/services/partner.ts (1 hunks)
  • packages/database/src/repository/partner.ts (2 hunks)
🧬 Code Graph Analysis (1)
packages/database/src/repository/partner.ts (2)
packages/database/src/types.ts (2)
  • PartnerLegalEntityDraft (16-16)
  • PartnerAgreementDraft (19-19)
packages/database/src/tables.ts (2)
  • partnerLegalEntities (91-99)
  • partnerAgreements (101-115)
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/database/src/repository/partner.ts (2)
packages/database/src/types.ts (2)
  • PartnerLegalEntityDraft (16-16)
  • PartnerAgreementDraft (19-19)
packages/database/src/tables.ts (2)
  • partnerLegalEntities (91-99)
  • partnerAgreements (101-115)
⏰ 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: build
🔇 Additional comments (10)
apps/web-app/app/components/PartnerCard.vue (1)

11-11: LGTM! Nice UX enhancement with smooth fade-in effect.

The addition of opacity-0 and duration-200 creates a smooth fade-in transition for the agreement progress overlay, improving the user experience with consistent hover animations.

apps/web-app/app/components/PartnerLegalEntityCard.vue (2)

2-19: LGTM! Clean conditional rendering and comprehensive entity display.

The entity information display is well-structured and includes all relevant legal entity details (name, INN, OGRNIP, comment) with appropriate styling.


28-36: Excellent TypeScript typing and component setup.

The props definition properly handles nullable states, and the modal management follows the established overlay pattern consistently.

packages/database/src/repository/partner.ts (4)

1-1: LGTM! Necessary type imports added.

The additional draft types are properly imported and required for the new repository methods.


4-4: LGTM! Required table imports added.

The partnerAgreements and partnerLegalEntities table imports are necessary for the new insert operations.


38-41: LGTM! Clean implementation following established patterns.

The createLegalEntity method correctly implements the database insert operation using Drizzle ORM and follows the same pattern as the existing create method.


43-46: LGTM! Consistent implementation pattern.

The createAgreement method follows the same clean pattern as createLegalEntity and existing repository methods, maintaining consistency across the codebase.

apps/web-app/i18n/locales/ru-RU.json (1)

30-30: LGTM!

The localization additions are well-structured and follow the existing naming conventions.

Also applies to: 355-360

apps/web-app/app/pages/partner/[id]/index.vue (1)

3-3: Good refactoring to improve code organization!

The extraction of legal entity and agreement display logic into dedicated components enhances maintainability and follows the single responsibility principle.

Also applies to: 45-55

apps/web-app/app/components/form/CreatePartnerLegalEntity.vue (1)

1-101: Well-implemented form component!

The component follows Vue 3 best practices with proper form validation, state management, and error handling. The user experience is enhanced with appropriate toast notifications.

>
<UPopover>
<UFormField
label="Дата заключения"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use internationalization consistently for all labels.

Some form labels are hard-coded in Russian while others use i18n. This should be consistent throughout the form.

-        label="Дата заключения"
+        :label="$t('partner.agreement.form.concludedAt')"

-        label="Дата окончания"
+        :label="$t('partner.agreement.form.willEndAt')"

-    <UFormField label="Номер договора (внутренний)" name="internalId">
+    <UFormField :label="$t('partner.agreement.form.internalId')" name="internalId">

-    <UFormField label="Роялти, %" name="royalty">
+    <UFormField :label="$t('partner.agreement.form.royalty')" name="royalty">

-    <UFormField label="Мин. роялти, руб" name="minRoyaltyPerMonth">
+    <UFormField :label="$t('partner.agreement.form.minRoyalty')" name="minRoyaltyPerMonth">

-    <UFormField label="Паушальный взнос, руб" name="lumpSumPayment">
+    <UFormField :label="$t('partner.agreement.form.lumpSumPayment')" name="lumpSumPayment">

Also applies to: 30-30, 48-48, 56-56, 66-66, 75-75

🤖 Prompt for AI Agents
In apps/web-app/app/components/form/CreatePartnerAgreement.vue at lines 10, 30,
48, 56, 66, and 75, the form labels are hard-coded in Russian instead of using
the internationalization (i18n) system. Replace these hard-coded label strings
with the appropriate i18n translation keys by using the $t function or
equivalent i18n method to ensure consistent localization across the form.

>
<UInput
:value="selectedConcludedAt ? df.format(selectedConcludedAt.toDate(getLocalTimeZone())) : ''"
placeholder="Выберите дату"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use internationalization for placeholder text.

Placeholder text should also use i18n for consistency.

-          placeholder="Выберите дату"
+          :placeholder="$t('common.selectDate')"

-        placeholder="Для внутреннего использования"
+        :placeholder="$t('partner.agreement.form.commentPlaceholder')"

Also applies to: 36-36, 88-88

🤖 Prompt for AI Agents
In apps/web-app/app/components/form/CreatePartnerAgreement.vue at lines 16, 36,
and 88, the placeholder text is hardcoded in Russian. Replace these hardcoded
placeholder strings with calls to the internationalization (i18n) function to
ensure the text is localized consistently across the app. Use the appropriate
i18n key for each placeholder and update the template bindings accordingly.

Comment on lines +138 to +147
watch(selectedConcludedAt, () => {
state.value.concludedAt = new Date(
`${selectedConcludedAt.value?.toString()} 12:00:00`,
).toISOString()
})
watch(selectedWillEndAt, () => {
state.value.willEndAt = new Date(
`${selectedWillEndAt.value?.toString()} 12:00:00`,
).toISOString()
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve date conversion logic for reliability.

The current date conversion logic manually constructs date strings which can be error-prone and fragile. Consider using proper date conversion methods.

 watch(selectedConcludedAt, () => {
-  state.value.concludedAt = new Date(
-    `${selectedConcludedAt.value?.toString()} 12:00:00`,
-  ).toISOString()
+  if (selectedConcludedAt.value) {
+    state.value.concludedAt = selectedConcludedAt.value.toDate(getLocalTimeZone()).toISOString()
+  }
 })
 watch(selectedWillEndAt, () => {
-  state.value.willEndAt = new Date(
-    `${selectedWillEndAt.value?.toString()} 12:00:00`,
-  ).toISOString()
+  if (selectedWillEndAt.value) {
+    state.value.willEndAt = selectedWillEndAt.value.toDate(getLocalTimeZone()).toISOString()
+  }
 })

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/web-app/app/components/form/CreatePartnerAgreement.vue around lines 138
to 147, the date conversion manually constructs date strings by appending
"12:00:00" which is fragile and error-prone. Instead, update the watchers to
convert the selected date values to Date objects directly using reliable methods
like setting the time explicitly or using date libraries if available, then
convert to ISO string. This will ensure consistent and accurate date handling.

Comment on lines +19 to +21
<h3 class="text-xl md:text-xl/6 font-semibold">
Договор №{{ agreement.internalId }}
</h3>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use internationalization for hard-coded Russian text.

Hard-coded Russian text should be moved to i18n locales for better maintainability and potential future localization.

-      <h3 class="text-xl md:text-xl/6 font-semibold">
-        Договор №{{ agreement.internalId }}
-      </h3>
+      <h3 class="text-xl md:text-xl/6 font-semibold">
+        {{ $t('partner.agreement.title', { id: agreement.internalId }) }}
+      </h3>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<h3 class="text-xl md:text-xl/6 font-semibold">
Договор №{{ agreement.internalId }}
</h3>
<h3 class="text-xl md:text-xl/6 font-semibold">
{{ $t('partner.agreement.title', { id: agreement.internalId }) }}
</h3>
🤖 Prompt for AI Agents
In apps/web-app/app/components/PartnerAgreementCard.vue around lines 19 to 21,
the hard-coded Russian text "Договор №" should be replaced with a call to the
internationalization (i18n) system. Move this text to the appropriate i18n
locale file and update the template to use the i18n key instead, ensuring the
text can be localized and maintained properly.

Comment on lines +24 to +41
<p v-if="agreement.concludedAt">
Заключен: {{ format(new Date(agreement.concludedAt), 'd MMMM yyyy', { locale: ru }) }}
</p>
<p v-if="agreement.willEndAt">
Действует до: {{ format(new Date(agreement.willEndAt), 'd MMMM yyyy', { locale: ru }) }}
</p>
<p>Роялти: {{ agreement.royalty }}%</p>
<p>Мин. роялти: {{ agreement.minRoyaltyPerMonth }} ₽ / месяц</p>

<p v-if="agreement.marketingFee">
Маркетинговый сбор: {{ agreement.marketingFee }}%
</p>
<p v-if="agreement.minMarketingFeePerMonth">
Мин. маркетинговый сбор: {{ agreement.minMarketingFeePerMonth }} ₽ / месяц
</p>

<p>Паушальный взнос: {{ agreement.lumpSumPayment }} ₽</p>
</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use internationalization for hard-coded Russian labels.

All the Russian labels should be extracted to i18n locales for consistency and maintainability.

-        <p v-if="agreement.concludedAt">
-          Заключен: {{ format(new Date(agreement.concludedAt), 'd MMMM yyyy', { locale: ru }) }}
-        </p>
-        <p v-if="agreement.willEndAt">
-          Действует до: {{ format(new Date(agreement.willEndAt), 'd MMMM yyyy', { locale: ru }) }}
-        </p>
-        <p>Роялти: {{ agreement.royalty }}%</p>
-        <p>Мин. роялти: {{ agreement.minRoyaltyPerMonth }} ₽ / месяц</p>
+        <p v-if="agreement.concludedAt">
+          {{ $t('partner.agreement.concluded') }}: {{ format(new Date(agreement.concludedAt), 'd MMMM yyyy', { locale: ru }) }}
+        </p>
+        <p v-if="agreement.willEndAt">
+          {{ $t('partner.agreement.validUntil') }}: {{ format(new Date(agreement.willEndAt), 'd MMMM yyyy', { locale: ru }) }}
+        </p>
+        <p>{{ $t('partner.agreement.royalty') }}: {{ agreement.royalty }}%</p>
+        <p>{{ $t('partner.agreement.minRoyalty') }}: {{ agreement.minRoyaltyPerMonth }} ₽ / {{ $t('common.month') }}</p>
🤖 Prompt for AI Agents
In apps/web-app/app/components/PartnerAgreementCard.vue between lines 24 and 41,
the Russian text labels are hard-coded directly in the template. To fix this,
replace all these hard-coded Russian strings with references to i18n locale keys
using the appropriate translation function or directive. Extract each label into
the i18n locale files to ensure consistency and maintainability across the app.

Comment thread apps/web-app/shared/services/partner.ts Outdated
import { type } from 'arktype'

export const createPartnerLegalEntitySchema = type({
inn: type('8 <= string <= 16').describe('error.length.invalid'),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix INN validation to match Russian tax number requirements.

INN (Individual Taxpayer Number) in Russia has specific length requirements: 10 digits for organizations, 12 digits for individuals. The current constraint of 8-16 characters is incorrect.

-  inn: type('8 <= string <= 16').describe('error.length.invalid'),
+  inn: type('/^\\d{10}$|^\\d{12}$/').describe('error.inn.invalid'),

-  inn: type('8 <= string <= 16 | undefined').describe('error.length.invalid').optional(),
+  inn: type('/^\\d{10}$|^\\d{12}$/ | undefined').describe('error.inn.invalid').optional(),

Also applies to: 12-12

🤖 Prompt for AI Agents
In apps/web-app/shared/services/partner.ts at line 4, the INN validation
currently allows strings between 8 and 16 characters, which is incorrect. Update
the validation to only accept strings of length 10 or 12 to match Russian tax
number requirements for organizations and individuals respectively. Adjust the
type or validation logic to enforce these exact lengths instead of a range.

Comment thread apps/web-app/shared/services/partner.ts Outdated

export const createPartnerLegalEntitySchema = type({
inn: type('8 <= string <= 16').describe('error.length.invalid'),
ogrnip: type('8 <= string <= 25 | undefined').describe('error.length.invalid').optional(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix OGRNIP validation to match Russian requirements.

OGRNIP (Primary State Registration Number of Individual Entrepreneur) in Russia must be exactly 15 digits, not 8-25 characters.

-  ogrnip: type('8 <= string <= 25 | undefined').describe('error.length.invalid').optional(),
+  ogrnip: type('/^\\d{15}$/ | undefined').describe('error.ogrnip.invalid').optional(),

-  ogrnip: type('8 <= string <= 25 | undefined').describe('error.length.invalid').optional(),
+  ogrnip: type('/^\\d{15}$/ | undefined').describe('error.ogrnip.invalid').optional(),

Also applies to: 13-13

🤖 Prompt for AI Agents
In apps/web-app/shared/services/partner.ts at line 5, the OGRNIP validation
currently allows strings between 8 and 25 characters, which is incorrect. Update
the validation to require exactly 15 digits for OGRNIP to comply with Russian
standards. Adjust the type or validation schema to enforce a fixed length of 15
characters instead of a range.

Comment thread apps/web-app/shared/services/partner.ts Outdated
inn: type('8 <= string <= 16').describe('error.length.invalid'),
ogrnip: type('8 <= string <= 25 | undefined').describe('error.length.invalid').optional(),
name: type('8 <= string <= 150').describe('error.length.invalid'),
comment: type('string | undefined').describe('error.length.invalid').optional(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use specific error messages for different field types.

All fields currently use the same generic error message. Different field types should have specific, meaningful error messages.

-  comment: type('string | undefined').describe('error.length.invalid').optional(),
+  comment: type('string | undefined').describe('error.comment.invalid').optional(),

Apply similar changes to all comment fields and consider adding length constraints:

+  comment: type('0 <= string <= 500 | undefined').describe('error.comment.tooLong').optional(),

Also applies to: 15-15, 27-27, 39-39

🤖 Prompt for AI Agents
In apps/web-app/shared/services/partner.ts at lines 7, 15, 27, and 39, the
comment fields use a generic error message for validation. Update each comment
field to use specific error messages tailored to the field type, replacing the
generic 'error.length.invalid' message with more descriptive ones. Additionally,
add appropriate length constraints to these fields to enforce validation rules
clearly.

Comment on lines +20 to +21
concludedAt: type('string').describe('error.length.invalid'),
willEndAt: type('string').describe('error.length.invalid').optional(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add proper date validation for agreement dates.

Date fields should validate ISO string format and ensure business rule compliance.

-  concludedAt: type('string').describe('error.length.invalid'),
-  willEndAt: type('string').describe('error.length.invalid').optional(),
+  concludedAt: type('/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z$/', 'string').describe('error.date.invalid'),
+  willEndAt: type('/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z$/ | undefined', 'string | undefined').describe('error.date.invalid').optional(),

-  concludedAt: type('string | undefined').describe('error.length.invalid').optional(),
-  willEndAt: type('string | undefined').describe('error.length.invalid').optional(),
+  concludedAt: type('/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z$/ | undefined', 'string | undefined').describe('error.date.invalid').optional(),
+  willEndAt: type('/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z$/ | undefined', 'string | undefined').describe('error.date.invalid').optional(),

Also applies to: 32-33

🤖 Prompt for AI Agents
In apps/web-app/shared/services/partner.ts at lines 20-21 and 32-33, the date
fields concludedAt and willEndAt currently only validate as strings without
enforcing ISO date format or business rules. Replace the simple string type
validation with a date validation that checks for ISO 8601 format compliance.
Additionally, implement any necessary business rule validations such as ensuring
concludedAt is before willEndAt or other domain-specific constraints. Use a date
validation library or custom validation logic to enforce these rules.

Comment thread apps/web-app/shared/services/partner.ts Outdated
Comment on lines +23 to +25
royalty: type('number').describe('error.length.invalid'),
minRoyaltyPerMonth: type('number').describe('error.length.invalid'),
lumpSumPayment: type('number').describe('error.length.invalid'),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add reasonable range validation for numerical fields.

Percentage and monetary fields should have reasonable bounds to prevent invalid business data.

-  royalty: type('number').describe('error.length.invalid'),
-  minRoyaltyPerMonth: type('number').describe('error.length.invalid'),
-  lumpSumPayment: type('number').describe('error.length.invalid'),
+  royalty: type('0 <= number <= 100').describe('error.percentage.invalid'),
+  minRoyaltyPerMonth: type('number >= 0').describe('error.amount.invalid'),
+  lumpSumPayment: type('number >= 0').describe('error.amount.invalid'),

-  royalty: type('number | undefined').describe('error.length.invalid').optional(),
-  minRoyaltyPerMonth: type('number | undefined').describe('error.length.invalid').optional(),
-  lumpSumPayment: type('number | undefined').describe('error.length.invalid').optional(),
+  royalty: type('0 <= number <= 100 | undefined').describe('error.percentage.invalid').optional(),
+  minRoyaltyPerMonth: type('number >= 0 | undefined').describe('error.amount.invalid').optional(),
+  lumpSumPayment: type('number >= 0 | undefined').describe('error.amount.invalid').optional(),

Also applies to: 35-37

🤖 Prompt for AI Agents
In apps/web-app/shared/services/partner.ts around lines 23 to 25 and 35 to 37,
the numerical fields royalty, minRoyaltyPerMonth, and lumpSumPayment currently
only have type validation without range checks. Add reasonable minimum and
maximum value validations to these fields to ensure the values fall within
acceptable business limits, such as percentages between 0 and 100 or monetary
amounts above zero, by extending the schema definitions with range constraints.

@sonarqubecloud
Copy link
Copy Markdown

@hmbanan666 hmbanan666 merged commit 702727a into main Jul 24, 2025
8 checks passed
@hmbanan666 hmbanan666 deleted the new-actions branch July 24, 2025 07:27
This was referenced Jul 24, 2025
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