Files
CRM_Montzar/apps/web/src/lib/activity-thread.ts
T
MahdiandCursor b03d97dd9e fix(activities): show each note as its own dated message
The colon inside the clock was splitting the stamp from the comment, so date, author, text, and attachments now stay together.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-09 16:39:06 +03:30

72 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { jalaliToDate } from '@/lib/jalali';
export type ActivityThreadEntry = {
at: Date | null;
dateLabel: string;
timeLabel: string;
author: string;
body: string;
};
const STAMP = /^\[(.+?) ساعت (\S+?)(?: — (.+))?\]$/;
/** یادداشت فعالیت را به نظرهای جدا با مهر تاریخ/ساعت/نویسنده تبدیل می‌کند. */
export function parseActivityThread(raw: string | null | undefined): ActivityThreadEntry[] | null {
const text = (raw ?? '').replace(/\r\n/g, '\n').trim();
if (!text) return null;
const entries: ActivityThreadEntry[] = [];
const preamble: string[] = [];
let current: ActivityThreadEntry | null = null;
let sawStamp = false;
for (const line of text.split('\n')) {
const match = line.trim().match(STAMP);
if (match) {
sawStamp = true;
if (current) entries.push(trimEntry(current));
current = {
at: stampToDate(match[1] ?? '', match[2] ?? ''),
dateLabel: (match[1] ?? '').trim(),
timeLabel: (match[2] ?? '').trim(),
author: (match[3] ?? '').trim(),
body: '',
};
continue;
}
if (current) current.body += `${current.body ? '\n' : ''}${line}`;
else preamble.push(line);
}
if (current) entries.push(trimEntry(current));
if (!sawStamp) return null;
const head = preamble.join('\n').trim();
if (head) {
entries.unshift({ at: null, dateLabel: '', timeLabel: '', author: '', body: head });
}
return entries;
}
function trimEntry(entry: ActivityThreadEntry): ActivityThreadEntry {
return { ...entry, body: entry.body.trim() };
}
function latinDigits(value: string): string {
const fa = '۰۱۲۳۴۵۶۷۸۹';
const ar = '٠١٢٣٤٥٦٧٨٩';
return value.replace(/[۰-۹٠-٩]/g, (ch) => {
const faIndex = fa.indexOf(ch);
return String(faIndex >= 0 ? faIndex : ar.indexOf(ch));
});
}
function stampToDate(dateLabel: string, timeLabel: string): Date | null {
const date = latinDigits(dateLabel).match(/(\d{4})\/(\d{1,2})\/(\d{1,2})/);
if (!date) return null;
const at = jalaliToDate(Number(date[1]), Number(date[2]), Number(date[3]));
if (Number.isNaN(at.getTime())) return null;
const time = latinDigits(timeLabel).match(/(\d{1,2}):(\d{2})/);
if (time) at.setHours(Number(time[1]), Number(time[2]), 0, 0);
return at;
}