diff --git a/.gitignore b/.gitignore
index 84ec3bc..90ab536 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,4 @@ docs/~$*
~$*
.idea/
.vscode/
+*.tsbuildinfo
diff --git a/apps/api/prisma/migrations/20260902160000_activity_priority_creator/migration.sql b/apps/api/prisma/migrations/20260902160000_activity_priority_creator/migration.sql
new file mode 100644
index 0000000..ca80bfd
--- /dev/null
+++ b/apps/api/prisma/migrations/20260902160000_activity_priority_creator/migration.sql
@@ -0,0 +1,14 @@
+-- CreateEnum
+CREATE TYPE "ActivityPriority" AS ENUM ('low', 'medium', 'high');
+
+-- AlterTable
+ALTER TABLE "Activity" ADD COLUMN "priority" "ActivityPriority" NOT NULL DEFAULT 'medium';
+ALTER TABLE "Activity" ADD COLUMN "createdById" TEXT;
+
+UPDATE "Activity" SET "createdById" = "assigneeId" WHERE "createdById" IS NULL;
+
+-- AddForeignKey
+ALTER TABLE "Activity" ADD CONSTRAINT "Activity_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
+
+-- CreateIndex
+CREATE INDEX "Activity_organizationId_createdById_idx" ON "Activity"("organizationId", "createdById");
diff --git a/apps/api/prisma/migrations/20260902183000_activity_duration_reminder_priority/migration.sql b/apps/api/prisma/migrations/20260902183000_activity_duration_reminder_priority/migration.sql
new file mode 100644
index 0000000..981313c
--- /dev/null
+++ b/apps/api/prisma/migrations/20260902183000_activity_duration_reminder_priority/migration.sql
@@ -0,0 +1,7 @@
+-- AlterEnum
+ALTER TYPE "ActivityPriority" ADD VALUE 'very_low';
+ALTER TYPE "ActivityPriority" ADD VALUE 'very_high';
+
+-- AlterTable
+ALTER TABLE "Activity" ADD COLUMN "durationMinutes" INTEGER;
+ALTER TABLE "Activity" ADD COLUMN "reminderAt" TIMESTAMP(3);
diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma
index a509fe5..3d973d4 100644
--- a/apps/api/prisma/schema.prisma
+++ b/apps/api/prisma/schema.prisma
@@ -33,6 +33,14 @@ enum ActivityStatus {
canceled
}
+enum ActivityPriority {
+ low
+ medium
+ high
+ very_low
+ very_high
+}
+
enum CustomFieldEntity {
person
company
@@ -165,6 +173,7 @@ model User {
ownedCompanies Company[] @relation("CompanyOwner")
ownedProjectCards ProjectCard[] @relation("ProjectCardOwner")
activities Activity[] @relation("ActivityAssignee")
+ createdActivities Activity[] @relation("ActivityCreator")
notes Note[] @relation("NoteAuthor")
noteAttachments NoteAttachment[] @relation("NoteAttachmentUploader")
notifications Notification[]
@@ -333,10 +342,13 @@ model Activity {
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
type ActivityType @default(task)
status ActivityStatus @default(open)
- title String
- description String?
- dueAt DateTime?
- completedAt DateTime?
+ priority ActivityPriority @default(medium)
+ title String
+ description String?
+ dueAt DateTime?
+ durationMinutes Int?
+ reminderAt DateTime?
+ completedAt DateTime?
personId String?
person Person? @relation(fields: [personId], references: [id], onDelete: SetNull)
companyId String?
@@ -345,11 +357,14 @@ model Activity {
deal Deal? @relation(fields: [dealId], references: [id], onDelete: SetNull)
assigneeId String?
assignee User? @relation("ActivityAssignee", fields: [assigneeId], references: [id], onDelete: SetNull)
+ createdById String?
+ createdBy User? @relation("ActivityCreator", fields: [createdById], references: [id], onDelete: SetNull)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([organizationId, status, dueAt])
@@index([organizationId, assigneeId])
+ @@index([organizationId, createdById])
@@index([personId])
@@index([companyId])
@@index([dealId])
diff --git a/apps/api/src/activities/activities.service.ts b/apps/api/src/activities/activities.service.ts
index be7f66b..9fe7481 100644
--- a/apps/api/src/activities/activities.service.ts
+++ b/apps/api/src/activities/activities.service.ts
@@ -22,9 +22,12 @@ type ActivityRow = {
id: string;
type: ActivityDto['type'];
status: ActivityDto['status'];
+ priority: ActivityDto['priority'];
title: string;
description: string | null;
dueAt: Date | null;
+ durationMinutes: number | null;
+ reminderAt: Date | null;
completedAt: Date | null;
createdAt: Date;
updatedAt: Date;
@@ -32,6 +35,7 @@ type ActivityRow = {
company: { id: string; name: string } | null;
deal: { id: string; title: string } | null;
assignee: { id: string; fullName: string } | null;
+ createdBy: { id: string; fullName: string } | null;
};
const include = {
@@ -39,6 +43,7 @@ const include = {
company: { select: { id: true, name: true } },
deal: { select: { id: true, title: true } },
assignee: { select: { id: true, fullName: true } },
+ createdBy: { select: { id: true, fullName: true } },
} satisfies Prisma.ActivityInclude;
@Injectable()
@@ -173,12 +178,16 @@ export class ActivitiesService {
organizationId,
title: dto.title.trim(),
type: dto.type ?? 'task',
+ priority: dto.priority ?? 'medium',
description: emptyToNull(dto.description),
dueAt: dto.dueAt ? new Date(dto.dueAt) : null,
+ durationMinutes: dto.durationMinutes ?? null,
+ reminderAt: dto.reminderAt ? new Date(dto.reminderAt) : null,
personId: emptyToNull(dto.personId ?? null),
companyId: emptyToNull(dto.companyId ?? null),
dealId: emptyToNull(dto.dealId ?? null),
assigneeId,
+ createdById: userId,
},
include,
});
@@ -211,8 +220,11 @@ export class ActivitiesService {
const data: Prisma.ActivityUncheckedUpdateInput = {};
if (dto.title !== undefined) data.title = dto.title.trim();
if (dto.type !== undefined) data.type = dto.type;
+ if (dto.priority !== undefined) data.priority = dto.priority;
if ('description' in dto) data.description = emptyToNull(dto.description);
if ('dueAt' in dto) data.dueAt = dto.dueAt ? new Date(dto.dueAt) : null;
+ if ('durationMinutes' in dto) data.durationMinutes = dto.durationMinutes ?? null;
+ if ('reminderAt' in dto) data.reminderAt = dto.reminderAt ? new Date(dto.reminderAt) : null;
if ('personId' in dto) data.personId = emptyToNull(dto.personId ?? null);
if ('companyId' in dto) data.companyId = emptyToNull(dto.companyId ?? null);
if ('dealId' in dto) data.dealId = emptyToNull(dto.dealId ?? null);
@@ -394,9 +406,12 @@ export class ActivitiesService {
id: row.id,
type: row.type,
status: row.status,
+ priority: row.priority,
title: row.title,
description: row.description,
dueAt: row.dueAt?.toISOString() ?? null,
+ durationMinutes: row.durationMinutes,
+ reminderAt: row.reminderAt?.toISOString() ?? null,
completedAt: row.completedAt?.toISOString() ?? null,
person: row.person
? {
@@ -407,6 +422,7 @@ export class ActivitiesService {
company: row.company,
deal: row.deal,
assignee: row.assignee,
+ createdBy: row.createdBy,
createdAt: row.createdAt.toISOString(),
updatedAt: row.updatedAt.toISOString(),
};
diff --git a/apps/api/src/activities/dto.ts b/apps/api/src/activities/dto.ts
index a9842ac..a6edd42 100644
--- a/apps/api/src/activities/dto.ts
+++ b/apps/api/src/activities/dto.ts
@@ -14,6 +14,7 @@ import {
const ACTIVITY_TYPES = ['call', 'meeting', 'task', 'note'] as const;
const ACTIVITY_STATUSES = ['open', 'done', 'canceled'] as const;
+const ACTIVITY_PRIORITIES = ['very_low', 'low', 'medium', 'high', 'very_high'] as const;
const ACTIVITY_FILTERS = ['today', 'overdue', 'upcoming', 'done', 'all'] as const;
export class ListActivitiesQueryDto {
@@ -71,6 +72,10 @@ export class CreateActivityDto {
@IsIn(ACTIVITY_TYPES)
type?: (typeof ACTIVITY_TYPES)[number];
+ @IsOptional()
+ @IsIn(ACTIVITY_PRIORITIES)
+ priority?: (typeof ACTIVITY_PRIORITIES)[number];
+
@IsOptional()
@IsString()
@MaxLength(2000)
@@ -81,6 +86,18 @@ export class CreateActivityDto {
@IsDateString()
dueAt?: string | null;
+ @IsOptional()
+ @Type(() => Number)
+ @IsInt()
+ @Min(1)
+ @Max(24 * 60)
+ durationMinutes?: number | null;
+
+ @IsOptional()
+ @ValidateIf((_, v) => v !== '' && v != null)
+ @IsDateString()
+ reminderAt?: string | null;
+
@IsOptional()
@ValidateIf((_, v) => v !== '' && v != null)
@IsString()
@@ -117,6 +134,10 @@ export class UpdateActivityDto {
@IsIn(ACTIVITY_STATUSES)
status?: (typeof ACTIVITY_STATUSES)[number];
+ @IsOptional()
+ @IsIn(ACTIVITY_PRIORITIES)
+ priority?: (typeof ACTIVITY_PRIORITIES)[number];
+
@IsOptional()
@IsString()
@MaxLength(2000)
@@ -127,6 +148,18 @@ export class UpdateActivityDto {
@IsDateString()
dueAt?: string | null;
+ @IsOptional()
+ @Type(() => Number)
+ @IsInt()
+ @Min(1)
+ @Max(24 * 60)
+ durationMinutes?: number | null;
+
+ @IsOptional()
+ @ValidateIf((_, v) => v !== '' && v != null)
+ @IsDateString()
+ reminderAt?: string | null;
+
@IsOptional()
@ValidateIf((_, v) => v !== '' && v != null)
@IsString()
diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json
index 7d214dd..9d85208 100644
--- a/apps/api/tsconfig.json
+++ b/apps/api/tsconfig.json
@@ -9,7 +9,6 @@
"target": "ES2021",
"sourceMap": true,
"outDir": "./dist",
- "baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strict": true,
diff --git a/apps/web/src/components/CustomFieldsSection.vue b/apps/web/src/components/CustomFieldsSection.vue
index 0b473f6..0da833d 100644
--- a/apps/web/src/components/CustomFieldsSection.vue
+++ b/apps/web/src/components/CustomFieldsSection.vue
@@ -1,6 +1,7 @@
+
+
+
+
+ {{ display || emptyLabel }}
+
+
+
+
+
+
+
+
+
+ {{ monthTitle }}
+
+
+
+
+
+
+
+ {{ day }}
+
+
+
+ {{ toFaDigits(cell.jd) }}
+
+
+
+
+
+
+ ساعت
+
+ {{ toFaDigits(String(h).padStart(2, '0')) }}
+
+
+
+ دقیقه
+
+ {{ toFaDigits(String(m).padStart(2, '0')) }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web/src/components/NoteAiSuggestionPanel.vue b/apps/web/src/components/NoteAiSuggestionPanel.vue
index 6b315ae..bdd0aa9 100644
--- a/apps/web/src/components/NoteAiSuggestionPanel.vue
+++ b/apps/web/src/components/NoteAiSuggestionPanel.vue
@@ -2,6 +2,7 @@
import { computed, reactive, ref, watch } from 'vue';
import axios from 'axios';
import { api } from '@/lib/api';
+import JalaliDateInput from '@/components/JalaliDateInput.vue';
import type { NoteAiPayloadDto, NoteAiSuggestionDto } from '@mento/shared';
const props = defineProps<{
@@ -190,7 +191,7 @@ function formatPayloadSummary(value: NoteAiPayloadDto | null) {
کار بعدی
-
+
@@ -275,7 +276,6 @@ header {
}
input[type='text'],
-input[type='datetime-local'],
textarea {
border: 1px solid var(--line);
border-radius: 10px;
diff --git a/apps/web/src/components/UiIcon.vue b/apps/web/src/components/UiIcon.vue
index 71d29c5..df069a5 100644
--- a/apps/web/src/components/UiIcon.vue
+++ b/apps/web/src/components/UiIcon.vue
@@ -12,6 +12,7 @@ defineProps<{
| 'sparkle'
| 'bell'
| 'calendar'
+ | 'calendar-plus'
| 'mail'
| 'megaphone'
| 'send'
@@ -19,7 +20,31 @@ defineProps<{
| 'warning'
| 'trend'
| 'phone'
- | 'trophy';
+ | 'trophy'
+ | 'chat'
+ | 'dots'
+ | 'dots-v'
+ | 'document'
+ | 'bag'
+ | 'clock'
+ | 'user-plus'
+ | 'plus'
+ | 'chevron'
+ | 'star'
+ | 'filter'
+ | 'lightbulb'
+ | 'building'
+ | 'user'
+ | 'flag'
+ | 'refresh'
+ | 'x'
+ | 'mic'
+ | 'trash'
+ | 'stop'
+ | 'check-plain'
+ | 'calendar-sun'
+ | 'calendar-edit'
+ | 'minus';
size?: number;
}>();
@@ -88,6 +113,22 @@ defineProps<{
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -122,6 +163,95 @@ defineProps<{
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web/src/components/UserAvatar.vue b/apps/web/src/components/UserAvatar.vue
new file mode 100644
index 0000000..12a3e73
--- /dev/null
+++ b/apps/web/src/components/UserAvatar.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+ {{ initials }}
+
+
+
+
diff --git a/apps/web/src/components/activities/ActivityCommentSheet.vue b/apps/web/src/components/activities/ActivityCommentSheet.vue
new file mode 100644
index 0000000..df65849
--- /dev/null
+++ b/apps/web/src/components/activities/ActivityCommentSheet.vue
@@ -0,0 +1,897 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ activity.title }}
+
+
+ {{ subtitle }}
+
+
+
+
+ {{ activity.assignee?.fullName || 'بدون مسئول' }}
+
+
+
+
+ {{ dueLabel }}
+
+
+
+
+
+
+
+
+
+
+
{{ voiceError }}
+
+
+
+ یادداشت شما فقط برای اعضای تیم و مدیران {{ orgName }} قابل مشاهده خواهد بود.
+
+
+
+
+ ثبت نظر
+
+ انصراف
+
+
+
+
+ در حال ضبط صدا...
+ {{ timerLabel }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ برای لغو، بیرون بکشید
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web/src/components/activities/ActivityPostponeSheet.vue b/apps/web/src/components/activities/ActivityPostponeSheet.vue
new file mode 100644
index 0000000..917bb48
--- /dev/null
+++ b/apps/web/src/components/activities/ActivityPostponeSheet.vue
@@ -0,0 +1,458 @@
+
+
+
+
+
+
+
+
+
+
+
+ زمان جدید برای این فعالیت را انتخاب کنید.
+
+
+
+ {{ item.label }}
+
+
+
+
+ {{ item.time }}
+
+
+
+
+
+ تنظیم زمان دلخواه
+ انتخاب تاریخ و ساعت
+
+
+
+
+
+
+
+
+
+
+ تاریخ و ساعت جدید
+
+
+
{{ customPreview }}
+
+
+
+
+ با تعویق انداختن، این فعالیت در زمان جدید به لیست فعالیتهای شما منتقل میشود.
+
+
+
+
+
+
+
+
diff --git a/apps/web/src/components/person-profile/ProfileEmptyState.vue b/apps/web/src/components/person-profile/ProfileEmptyState.vue
new file mode 100644
index 0000000..bb191c2
--- /dev/null
+++ b/apps/web/src/components/person-profile/ProfileEmptyState.vue
@@ -0,0 +1,196 @@
+
+
+
+
+
+
✦
+
✦
+
✦
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ title }}
+
{{ description }}
+
+
+
+ +
+ {{ primaryLabel }}
+
+
+
+ {{ secondaryLabel }}
+
+
+
+
+
+
diff --git a/apps/web/src/components/person-profile/ProfileMoreMenu.vue b/apps/web/src/components/person-profile/ProfileMoreMenu.vue
new file mode 100644
index 0000000..cc76c8d
--- /dev/null
+++ b/apps/web/src/components/person-profile/ProfileMoreMenu.vue
@@ -0,0 +1,215 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
+
diff --git a/apps/web/src/layouts/AppLayout.vue b/apps/web/src/layouts/AppLayout.vue
index 8ad0d38..c9dc98c 100644
--- a/apps/web/src/layouts/AppLayout.vue
+++ b/apps/web/src/layouts/AppLayout.vue
@@ -24,14 +24,6 @@ const showOwnRecordsBanner = computed(
const roleLabel = computed(() =>
auth.user?.role ? USER_ROLE_LABELS[auth.user.role] : '',
);
-const userInitials = computed(() => {
- const parts = auth.user?.fullName?.trim().split(/\s+/) ?? [];
- if (!parts.length) return 'م';
- return parts
- .slice(0, 2)
- .map((p) => p[0])
- .join('');
-});
const todayLabel = computed(() => formatJalaliShort(new Date()));
const salesOpen = ref(false);
@@ -344,8 +336,14 @@ onUnmounted(() => {
اعتبار تا
-
+
ارز
diff --git a/apps/web/src/views/ReportsView.vue b/apps/web/src/views/ReportsView.vue
index 364e8cb..fcaa773 100644
--- a/apps/web/src/views/ReportsView.vue
+++ b/apps/web/src/views/ReportsView.vue
@@ -3,6 +3,8 @@ import { onMounted, ref } from 'vue';
import axios from 'axios';
import { api } from '@/lib/api';
import { formatMoney } from '@/lib/money';
+import { formatLocalDateInput } from '@/lib/jalali';
+import JalaliDateInput from '@/components/JalaliDateInput.vue';
import type { OrganizationDto, ReportsDashboardDto } from '@mento/shared';
const loading = ref(false);
@@ -23,8 +25,8 @@ const typeLabel: Record
= {
function defaultRange() {
const now = new Date();
const start = new Date(now.getFullYear(), now.getMonth(), 1);
- from.value = start.toISOString().slice(0, 10);
- to.value = now.toISOString().slice(0, 10);
+ from.value = formatLocalDateInput(start);
+ to.value = formatLocalDateInput(now);
}
function money(n: number) {
@@ -76,11 +78,11 @@ onMounted(() => {
از تاریخ
-
+
تا تاریخ
-
+
یادداشت
diff --git a/apps/web/src/views/TicketFormView.vue b/apps/web/src/views/TicketFormView.vue
index 67beb36..cfb0c34 100644
--- a/apps/web/src/views/TicketFormView.vue
+++ b/apps/web/src/views/TicketFormView.vue
@@ -150,6 +150,11 @@ async function load() {
assigneeId.value = auth.user?.id ?? '';
csatScore.value = null;
csatComment.value = '';
+ if (typeof route.query.personId === 'string' && route.query.personId) {
+ personId.value = route.query.personId;
+ const person = people.value.find((p) => p.id === personId.value);
+ if (person?.company?.id) companyId.value = person.company.id;
+ }
void loadFaqHints();
}
} catch {
diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json
index b2e538d..6d8a6a4 100644
--- a/apps/web/tsconfig.json
+++ b/apps/web/tsconfig.json
@@ -17,8 +17,7 @@
"noFallthroughCasesInSwitch": true,
"paths": {
"@/*": ["./src/*"]
- },
- "baseUrl": "."
+ }
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}
diff --git a/docs/راهنمای-کاربری.md b/docs/راهنمای-کاربری.md
index 4877a56..c538ad5 100644
--- a/docs/راهنمای-کاربری.md
+++ b/docs/راهنمای-کاربری.md
@@ -187,7 +187,7 @@
### بالای منو و پایین منو
- **کنار برند منتو:** آیکون اعلان + شمارنده خواندهنشده
-- **پایین منو:** نام کاربر و نقش در یک ردیف + دکمه **خروج**
+- **پایین منو:** کارت پروفایل (نام، نقش، آنلاین) — با کلیک، پروفایل خودتان باز میشود + دکمه **خروج**
اگر نقش شما فروش یا پشتیبانی باشد، ممکن است ببینید: «فقط پروندههای خودتان را میبینید.»
@@ -273,16 +273,20 @@
5. اگر فیلد سفارشی تعریف شده، پایین فرم پر کنید.
6. **ذخیره** را بزنید.
-روی پرونده ذخیرهشده:
+روی نام شخص در لیست، **پروفایل مشتری** باز میشود (خلاصه هوشمند، تبها، معاملات و تایملاین). برای ویرایش فیلدها از منوی سهنقطه → **ویرایش اطلاعات** استفاده کنید.
-- بخش **باشگاه مشتریان** امتیاز و سطح را نشان میدهد (لینک به مدیریت باشگاه).
-- پایین صفحه **تایملاین** ظاهر میشود.
+روی پرونده:
+
+- تماس / پیام / پیگیری از ردیف اکشنها
+- تبهای خلاصه، فعالیتها، معاملات، کارتها، اطلاعات
+- پایین صفحه: فعالیت جدید، ایجاد سریع، تماس سریع
+- بخش **باشگاه مشتریان** در تب اطلاعات (لینک به مدیریت باشگاه)
### ویرایش یا حذف شخص
-1. در لیست روی نام کلیک کنید.
-2. ویرایش → **ذخیره**.
-3. برای حذف: **حذف** و تأیید (مراقب باشید؛ ارتباطها تحت تأثیر قرار میگیرند).
+1. در لیست روی نام کلیک کنید تا پروفایل باز شود.
+2. منوی سهنقطه → **ویرایش اطلاعات** → ذخیره.
+3. برای حذف: همان منو → **حذف شخص** و تأیید (مراقب باشید؛ ارتباطها تحت تأثیر قرار میگیرند).
### ثبت و ویرایش شرکت
@@ -413,7 +417,7 @@
## ۷) تایملاین پرونده
-روی پرونده **ذخیرهشده** شخص، شرکت یا معامله، پایین فرم بخش **تایملاین** هست.
+روی پروفایل **شخص** تایملاین در تب خلاصه و فعالیتهاست. روی پرونده **شرکت** یا **معامله**، پایین فرم بخش **تایملاین** هست.
نشان میدهد:
diff --git a/docs/وضعیت-پیشرفت.md b/docs/وضعیت-پیشرفت.md
index 33be4c4..f4cda8a 100644
--- a/docs/وضعیت-پیشرفت.md
+++ b/docs/وضعیت-پیشرفت.md
@@ -1,7 +1,7 @@
# وضعیت پیشرفت منتو (Mento CRM)
سند زندهٔ کارها — با هر قابلیت جدید همین فایل بهروز میشود.
-آخرین بهروزرسانی: ۸ شهریور ۱۴۰۵
+آخرین بهروزرسانی: ۱۱ شهریور ۱۴۰۵
**راهنما:**
- `[x]` انجام شده
@@ -14,6 +14,7 @@
| تاریخ | مورد |
|---|---|
+| ۱۴۰۵/۶/۱۱ | پروفایل مشتری مطابق ماکاپ (خلاصه هوشمند، تبها، KPI، تایملاین) + پروفایل کاربر از کارت سایدبار |
| ۱۴۰۵/۶/۸ | تکمیل کامل راهنمای کاربری (همه ماژولها، منوی جدید، پرتال/SMS، پروژه، باشگاه، اتوماسیون) |
| ۱۴۰۵/۶/۸ | کاریز پروژه + باشگاه مشتریان + اتوماسیون اگر/آنگاه |
| ۱۴۰۵/۶/۸ | تکمیل `deploy/DEPLOY.md`: پورت DB ۱۵۴۳۲، Prisma از `apps/api`، symlink `@mento/shared`، SMS ترِز، تلههای tarball/nginx |
@@ -44,7 +45,7 @@
| وضعیت | مورد | توضیح |
|---|---|---|
| [x] | ورود / نشست / سازمان | حساب دمو و JWT |
-| [x] | اشخاص و شرکتها | جستجو + جلوگیری از تکراری |
+| [x] | اشخاص و شرکتها | جستجو + جلوگیری از تکراری؛ پروفایل مشتری با خلاصه هوشمند |
| [x] | فیلد سفارشی | تعریف + مقدار روی شخص/شرکت/معامله |
| [x] | کاریز کانبان | کشیدن معامله بین مراحل |
| [x] | چند کاریز | انتخاب کاریز + ساخت کاریز جدید |
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
index 48b8256..b9c6aab 100644
--- a/packages/shared/src/index.ts
+++ b/packages/shared/src/index.ts
@@ -318,6 +318,7 @@ export interface MoveDealRequest {
export type ActivityType = "call" | "meeting" | "task" | "note";
export type ActivityStatus = "open" | "done" | "canceled";
+export type ActivityPriority = "very_low" | "low" | "medium" | "high" | "very_high";
export type ActivityFilter = "today" | "overdue" | "upcoming" | "done" | "all";
export type CustomFieldEntity = "person" | "company" | "deal";
@@ -366,14 +367,18 @@ export interface ActivityDto {
id: string;
type: ActivityType;
status: ActivityStatus;
+ priority: ActivityPriority;
title: string;
description: string | null;
dueAt: string | null;
+ durationMinutes: number | null;
+ reminderAt: string | null;
completedAt: string | null;
person: PersonSummaryDto | null;
company: CompanySummaryDto | null;
deal: DealSummaryDto | null;
assignee: UserSummaryDto | null;
+ createdBy: UserSummaryDto | null;
createdAt: string;
updatedAt: string;
}
@@ -381,8 +386,11 @@ export interface ActivityDto {
export interface CreateActivityRequest {
title: string;
type?: ActivityType;
+ priority?: ActivityPriority;
description?: string;
dueAt?: string | null;
+ durationMinutes?: number | null;
+ reminderAt?: string | null;
personId?: string | null;
companyId?: string | null;
dealId?: string | null;
@@ -393,8 +401,11 @@ export interface UpdateActivityRequest {
title?: string;
type?: ActivityType;
status?: ActivityStatus;
+ priority?: ActivityPriority;
description?: string | null;
dueAt?: string | null;
+ durationMinutes?: number | null;
+ reminderAt?: string | null;
personId?: string | null;
companyId?: string | null;
dealId?: string | null;