Rebrand the product to Montact and point local UI at production API.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-07 11:49:54 +03:30
co-authored by Cursor
parent 8357676e6c
commit 48d3469eea
56 changed files with 309 additions and 216 deletions
+4 -3
View File
@@ -1,5 +1,6 @@
import { PrismaClient } from '@prisma/client';
import * as bcrypt from 'bcryptjs';
import { brand } from '@mento/shared';
const prisma = new PrismaClient();
@@ -20,7 +21,7 @@ async function main() {
if (org) {
org = await prisma.organization.update({
where: { id: org.id },
data: { slug: 'demo-mento', name: 'سازمان نمونه منتو' },
data: { slug: 'demo-mento', name: `سازمان نمونه ${brand.nameFa}` },
});
console.log('Renamed demo org slug to demo-mento');
}
@@ -30,7 +31,7 @@ async function main() {
const passwordHash = await bcrypt.hash('demo1234', 10);
org = await prisma.organization.create({
data: {
name: 'سازمان نمونه منتو',
name: `سازمان نمونه ${brand.nameFa}`,
slug: 'demo-mento',
baseCurrency: 'IRR',
enabledCurrencies: ['IRR', 'USD', 'EUR'],
@@ -747,7 +748,7 @@ async function main() {
data: [
{
organizationId: org.id,
name: 'لایسنس سازمانی منتو',
name: `لایسنس سازمانی ${brand.nameFa}`,
sku: 'MENTO-ORG',
unitPrice: 80_000_000,
currency: 'IRR',
+4 -4
View File
@@ -11,7 +11,7 @@ import {
noteAccessWhere,
ownerWhere,
} from '../common/record-scope';
import { BUSINESS_FIELD_LABELS, isBusinessField } from '@mento/shared';
import { BUSINESS_FIELD_LABELS, brand, brandSuggestionFa, isBusinessField } from '@mento/shared';
import { AiService } from './ai.service';
type CatalogItem = AskMentoSourceDto;
@@ -39,7 +39,7 @@ export class AiAskService {
answer?: string;
sources?: Array<{ type?: string; id?: string; label?: string }>;
}>(
`You are Mento, a Persian CRM sales assistant.
`You are ${brand.name}, a Persian CRM sales assistant.
Answer ONLY from the CRM context. If the context is insufficient, say so in Persian.
Do not invent customers, amounts, dates, or promises.
Return JSON: { "answer": string, "sources": [{ "type": "person"|"note"|"activity"|"deal"|"memory", "id": string, "label": string }] }
@@ -73,7 +73,7 @@ Return JSON: { "text": string, "cta": string, "ctaKind": "call"|"activity" }
Rules:
- 2 to 4 short sentences from the context only.
- Mention durable memory, last contact, open deals, or next follow-up when present.
- cta is one concrete next step, starting with «پیشنهاد منتو:».
- cta is one concrete next step, starting with «${brand.suggestionPrefixFa}».
- ctaKind is "call" if a phone follow-up is best, otherwise "activity".
- Do not invent facts.`,
`Customer briefing context:\n${context}`,
@@ -84,7 +84,7 @@ Rules:
const ctaKind = raw.ctaKind === 'activity' ? 'activity' : 'call';
return {
text: text || 'هنوز دادهٔ کافی برای خلاصه هوشمند این مشتری نیست.',
cta: cta || 'پیشنهاد منتو: امروز با مشتری تماس بگیر',
cta: cta || brandSuggestionFa('امروز با مشتری تماس بگیر'),
ctaKind,
};
}
+2 -1
View File
@@ -1,5 +1,6 @@
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { brand } from '@mento/shared';
/** پنل ترِز / رایگان — همان الگوی montime */
const SMS_TREZ_DEFAULT_BASE_URL = 'http://smspanel.Trez.ir';
@@ -44,7 +45,7 @@ export class SmsService {
async sendOtpSms(mobile: string, code: string): Promise<boolean> {
const template =
this.config.get<string>('SMS_OTP_TEMPLATE') ??
'کد ورود پرتال منتو: {code}';
`کد ورود پرتال ${brand.nameFa}: {code}`;
const text = template.replace('{code}', code);
const result = await this.sendSmsDetailed(mobile, text);
return result.ok;
@@ -21,6 +21,7 @@ import type { Actor } from '../common/record-scope';
import { CompaniesService } from './companies.service';
import { CreateCompanyDto, ListQueryDto, UpdateCompanyDto } from './dto';
import { ContactsExcelService } from './contacts-excel.service';
import { brand } from '@mento/shared';
@Controller('companies')
@UseGuards(JwtAuthGuard)
@@ -50,7 +51,7 @@ export class CompaniesController {
res.set({
'Content-Type':
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'Content-Disposition': 'attachment; filename="mento-companies.xlsx"',
'Content-Disposition': `attachment; filename="${brand.filePrefix}-companies.xlsx"`,
});
return new StreamableFile(buffer);
}
+2 -1
View File
@@ -21,6 +21,7 @@ import type { Actor } from '../common/record-scope';
import { CreatePersonDto, ListQueryDto, UpdatePersonDto } from './dto';
import { PeopleService } from './people.service';
import { ContactsExcelService } from './contacts-excel.service';
import { brand } from '@mento/shared';
@Controller('people')
@UseGuards(JwtAuthGuard)
@@ -51,7 +52,7 @@ export class PeopleController {
res.set({
'Content-Type':
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'Content-Disposition': 'attachment; filename="mento-people.xlsx"',
'Content-Disposition': `attachment; filename="${brand.filePrefix}-people.xlsx"`,
});
return new StreamableFile(buffer);
}
+2 -1
View File
@@ -1,5 +1,6 @@
import { Controller, Get } from '@nestjs/common';
import type { HealthResponse } from '@mento/shared';
import { brand } from '@mento/shared';
@Controller('health')
export class HealthController {
@@ -7,7 +8,7 @@ export class HealthController {
getHealth(): HealthResponse {
return {
status: 'ok',
service: 'mento-api',
service: `${brand.id}-api`,
timestamp: new Date().toISOString(),
};
}
+2 -1
View File
@@ -29,6 +29,7 @@ import {
UpdateLoyaltyTierDto,
} from './dto';
import { LoyaltyService } from './loyalty.service';
import { brand } from '@mento/shared';
@Controller('loyalty')
@UseGuards(JwtAuthGuard, RolesGuard)
@@ -96,7 +97,7 @@ export class LoyaltyController {
res.set({
'Content-Type':
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'Content-Disposition': 'attachment; filename="mento-loyalty-members.xlsx"',
'Content-Disposition': `attachment; filename="${brand.filePrefix}-loyalty-members.xlsx"`,
});
return new StreamableFile(buffer);
}
+2 -1
View File
@@ -1,6 +1,7 @@
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { brand } from '@mento/shared';
import { AppModule } from './app.module';
async function bootstrap() {
@@ -24,7 +25,7 @@ async function bootstrap() {
const port = Number(config.get<string>('PORT') ?? 3000);
await app.listen(port);
// eslint-disable-next-line no-console
console.log(`Mento API listening on http://localhost:${port}/api`);
console.log(`${brand.name} API listening on http://localhost:${port}/api`);
}
bootstrap();
@@ -1,4 +1,5 @@
import { Injectable, Logger } from '@nestjs/common';
import { brand } from '@mento/shared';
import type { NotificationDto, NotificationsListDto } from '@mento/shared';
import { PrismaService } from '../prisma/prisma.service';
import { SmsService } from '../common/sms.service';
@@ -228,7 +229,7 @@ export class NotificationsService {
this.logger.warn('Activity reminder SMS skipped: SMS not configured');
return;
}
const text = `یادآوری منتو: ${title}`.slice(0, 160);
const text = `${brand.reminderPrefixFa} ${title}`.slice(0, 160);
const result = await this.sms.sendSmsDetailed(mobile, text);
if (!result.ok) {
this.logger.warn(`Activity reminder SMS failed: ${result.reason ?? 'unknown'}`);