Performing logins, mobile cards, and other tasks
This commit is contained in:
@@ -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"
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -85,6 +85,10 @@ export class BusinessEntity {
|
|||||||
@Column({ type: 'varchar', length: 20, default: 'first_booking' })
|
@Column({ type: 'varchar', length: 20, default: 'first_booking' })
|
||||||
promoDiscountMode!: 'first_booking' | 'always';
|
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 })
|
@Column({ name: 'logo_light_url', type: 'varchar', length: 500, nullable: true })
|
||||||
logoLightUrl!: string | null;
|
logoLightUrl!: string | null;
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { AssignBusinessOwnerDto } from './dto/assign-business-owner.dto';
|
|||||||
import { CreateBusinessDto } from './dto/create-business.dto';
|
import { CreateBusinessDto } from './dto/create-business.dto';
|
||||||
import { SetAvailabilityDto } from './dto/set-availability.dto';
|
import { SetAvailabilityDto } from './dto/set-availability.dto';
|
||||||
import { UpdateBusinessDto } from './dto/update-business.dto';
|
import { UpdateBusinessDto } from './dto/update-business.dto';
|
||||||
|
import { sanitizeBusinessHighlightKeys } from '../../common/constants/business-highlights';
|
||||||
|
|
||||||
type FindAllFilters = {
|
type FindAllFilters = {
|
||||||
categoryId?: string;
|
categoryId?: string;
|
||||||
@@ -165,6 +166,11 @@ export class BusinessesService {
|
|||||||
|
|
||||||
Object.assign(business, payload);
|
Object.assign(business, payload);
|
||||||
|
|
||||||
|
if (payload.heroHighlightKeys !== undefined) {
|
||||||
|
const sanitized = sanitizeBusinessHighlightKeys(payload.heroHighlightKeys);
|
||||||
|
business.heroHighlightKeys = sanitized.length ? sanitized : null;
|
||||||
|
}
|
||||||
|
|
||||||
if (business.promoDiscountEnabled) {
|
if (business.promoDiscountEnabled) {
|
||||||
if (
|
if (
|
||||||
!business.promoDiscountPercent ||
|
!business.promoDiscountPercent ||
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
import {
|
import {
|
||||||
|
IsArray,
|
||||||
|
ArrayMaxSize,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
IsInt,
|
IsInt,
|
||||||
IsLatitude,
|
IsLatitude,
|
||||||
@@ -179,4 +181,15 @@ export class UpdateBusinessDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
@Matches(/^(first_booking|always)$/)
|
@Matches(/^(first_booking|always)$/)
|
||||||
promoDiscountMode?: '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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user