feat(ui): modals close only via Esc/X/cancel; standard 3:1 form cover processing

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-07 15:23:14 +03:30
co-authored by Cursor
parent f5b6a47f3c
commit 2f3646d085
19 changed files with 366 additions and 117 deletions
+17 -2
View File
@@ -25,6 +25,13 @@ import {
} from '../contacts/normalize';
import { LeadStagesService } from '../leads/lead-stages.service';
const COVER_CONTENT_TYPES: Record<string, string> = {
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
png: 'image/png',
webp: 'image/webp',
};
const DEFAULT_FIELDS: LeadFormFieldInput[] = [
{
key: 'fullName',
@@ -222,7 +229,7 @@ export class LeadFormsService {
submitColor: row.submitColor || '#14b8a6',
hasCover: Boolean(row.coverStorageKey),
coverPath: row.coverStorageKey
? `/api/public/forms/${row.publicToken}/cover`
? `/api/public/forms/${row.publicToken}/cover?v=${row.updatedAt.getTime()}`
: null,
fields: row.fields.map((f) => ({
key: f.key,
@@ -806,7 +813,10 @@ export class LeadFormsService {
.toLowerCase()
.replace(/[^a-z0-9]/g, '')
.slice(0, 6);
const key = `lead-forms/${id}/cover.${ext || 'jpg'}`;
if (!COVER_CONTENT_TYPES[ext]) {
throw new BadRequestException('فرمت تصویر کاور باید PNG، JPG یا WEBP باشد');
}
const key = `lead-forms/${id}/cover.${ext}`;
await this.files.save(key, file.buffer);
if (row.coverStorageKey && row.coverStorageKey !== key) {
await this.files.remove(row.coverStorageKey);
@@ -832,6 +842,11 @@ export class LeadFormsService {
return row.coverStorageKey;
}
coverContentType(key: string) {
const ext = key.split('.').pop()?.toLowerCase() ?? '';
return COVER_CONTENT_TYPES[ext] ?? 'image/jpeg';
}
async getByToken(token: string, onlyActive = false) {
return this.prisma.leadForm.findFirst({
where: { publicToken: token, ...(onlyActive ? { isActive: true } : {}) },