Performing logins, mobile cards, and other tasks

This commit is contained in:
2026-07-16 16:48:13 +03:30
parent 61c8653af6
commit 67506e2d98
5 changed files with 89 additions and 0 deletions
@@ -0,0 +1,47 @@
/** Shared catalog of landing hero highlights (platform-wide, not per-category). */
export const BUSINESS_HIGHLIGHT_KEYS = [
'customer_satisfaction',
'professional_staff',
'online_booking',
'fair_pricing',
'clean_space',
'modern_equipment',
'quality_materials',
'fast_service',
'experienced_team',
'easy_access',
'secure_payment',
'friendly_support',
] as const;
export type BusinessHighlightKey = (typeof BUSINESS_HIGHLIGHT_KEYS)[number];
export const DEFAULT_BUSINESS_HIGHLIGHT_KEYS: BusinessHighlightKey[] = [
'customer_satisfaction',
'professional_staff',
'online_booking',
];
const KEY_SET = new Set<string>(BUSINESS_HIGHLIGHT_KEYS);
export function isBusinessHighlightKey(value: string): value is BusinessHighlightKey {
return KEY_SET.has(value);
}
/** Keep at most 3 unique valid keys; empty/invalid input → empty array (caller may fall back to defaults). */
export function sanitizeBusinessHighlightKeys(
keys: string[] | null | undefined,
): BusinessHighlightKey[] {
if (!keys?.length) return [];
const seen = new Set<string>();
const result: BusinessHighlightKey[] = [];
for (const raw of keys) {
const key = String(raw).trim();
if (!isBusinessHighlightKey(key) || seen.has(key)) continue;
seen.add(key);
result.push(key);
if (result.length >= 3) break;
}
return result;
}
@@ -0,0 +1,19 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddBusinessHeroHighlightKeys1789047600000 implements MigrationInterface {
name = 'AddBusinessHeroHighlightKeys1789047600000';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE "businesses"
ADD COLUMN IF NOT EXISTS "hero_highlight_keys" jsonb
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE "businesses"
DROP COLUMN IF EXISTS "hero_highlight_keys"
`);
}
}
+4
View File
@@ -85,6 +85,10 @@ export class BusinessEntity {
@Column({ type: 'varchar', length: 20, default: 'first_booking' })
promoDiscountMode!: 'first_booking' | 'always';
/** Up to 3 keys from the platform highlight catalog for the landing hero. */
@Column({ name: 'hero_highlight_keys', type: 'jsonb', nullable: true })
heroHighlightKeys!: string[] | null;
@Column({ name: 'logo_light_url', type: 'varchar', length: 500, nullable: true })
logoLightUrl!: string | null;
@@ -27,6 +27,7 @@ import { AssignBusinessOwnerDto } from './dto/assign-business-owner.dto';
import { CreateBusinessDto } from './dto/create-business.dto';
import { SetAvailabilityDto } from './dto/set-availability.dto';
import { UpdateBusinessDto } from './dto/update-business.dto';
import { sanitizeBusinessHighlightKeys } from '../../common/constants/business-highlights';
type FindAllFilters = {
categoryId?: string;
@@ -165,6 +166,11 @@ export class BusinessesService {
Object.assign(business, payload);
if (payload.heroHighlightKeys !== undefined) {
const sanitized = sanitizeBusinessHighlightKeys(payload.heroHighlightKeys);
business.heroHighlightKeys = sanitized.length ? sanitized : null;
}
if (business.promoDiscountEnabled) {
if (
!business.promoDiscountPercent ||
@@ -1,5 +1,7 @@
import { ApiPropertyOptional } from '@nestjs/swagger';
import {
IsArray,
ArrayMaxSize,
IsBoolean,
IsInt,
IsLatitude,
@@ -179,4 +181,15 @@ export class UpdateBusinessDto {
@IsString()
@Matches(/^(first_booking|always)$/)
promoDiscountMode?: 'first_booking' | 'always';
@ApiPropertyOptional({
type: [String],
example: ['customer_satisfaction', 'professional_staff', 'online_booking'],
description: 'Up to 3 highlight keys from the platform catalog for the landing hero',
})
@IsOptional()
@IsArray()
@ArrayMaxSize(3)
@IsString({ each: true })
heroHighlightKeys?: string[] | null;
}