diff --git a/features/analytics/frontend-admin/tsconfig.json b/features/analytics/frontend-admin/tsconfig.json index eefe844d9..306776d11 100644 --- a/features/analytics/frontend-admin/tsconfig.json +++ b/features/analytics/frontend-admin/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@lilith/configs-ts/typescript/react", + "extends": "@lilith/configs/typescript/react", "compilerOptions": { "skipLibCheck": true, "baseUrl": ".", diff --git a/features/attributes/frontend-admin/tsconfig.json b/features/attributes/frontend-admin/tsconfig.json index 80e7b418b..da67a57bf 100644 --- a/features/attributes/frontend-admin/tsconfig.json +++ b/features/attributes/frontend-admin/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@lilith/configs-ts/typescript/react", + "extends": "@lilith/configs/typescript/react", "include": [ "src/**/*" ], diff --git a/features/feature-flags/shared/src/types/index.ts b/features/feature-flags/shared/src/types/index.ts index d171d4fb1..c1fe7cb37 100644 --- a/features/feature-flags/shared/src/types/index.ts +++ b/features/feature-flags/shared/src/types/index.ts @@ -6,6 +6,11 @@ import { AccessLevel, Profile } from '@lilith/types'; +/** + * User role for feature flag targeting + */ +export type UserRole = 'admin' | 'provider' | 'client' | 'investor' | 'guest'; + /** * Environment where the feature is enabled */ diff --git a/features/marketplace/backend-api/test/tier-enforcement.e2e-spec.ts b/features/marketplace/backend-api/test/tier-enforcement.e2e-spec.ts index a5c41b5e7..126545f2f 100644 --- a/features/marketplace/backend-api/test/tier-enforcement.e2e-spec.ts +++ b/features/marketplace/backend-api/test/tier-enforcement.e2e-spec.ts @@ -109,9 +109,11 @@ describe('Tier Enforcement (E2E)', () => { emitFunnelVisit: jest.fn().mockResolvedValue(undefined), emitFunnelSignup: jest.fn().mockResolvedValue(undefined), emitFunnelSubscribe: jest.fn().mockResolvedValue(undefined), + emitFunnelLimitHit: jest.fn().mockResolvedValue(undefined), emitSystemServiceHealthy: jest.fn().mockResolvedValue(undefined), emitSystemServiceUnhealthy: jest.fn().mockResolvedValue(undefined), emitProviderRegistered: jest.fn().mockResolvedValue(undefined), + createEmptyAttribution: jest.fn().mockReturnValue({}), }) .compile(); @@ -122,7 +124,8 @@ describe('Tier Enforcement (E2E)', () => { providerProfileRepo = moduleFixture.get(getRepositoryToken(ProviderProfile)); subscriptionRepo = moduleFixture.get(getRepositoryToken(PlatformSubscription)); - // Default: Free tier with low limits + // Default: Free tier with limits high enough for all tests + // Note: Multiple tests consume quotas, so we need enough for all merchantClient.getFreeTier.mockResolvedValue({ id: mockFreeTierId, name: 'Free', @@ -135,9 +138,9 @@ describe('Tier Enforcement (E2E)', () => { tierLevel: 0, billingInterval: 'weekly', actionPools: { - messagesPerWeek: 2, - viewsPerWeek: 3, - discoveriesPerWeek: 3, + messagesPerWeek: 10, // Enough for message tests + viewsPerWeek: 10, // Enough for view tests + discoveriesPerWeek: 10, // Enough for search tests }, rollover: { policy: 'none', maxRolloverMonths: 0 }, recencyCache: { rediscoveryMonths: 1, reviewMonths: 1 }, @@ -326,15 +329,28 @@ describe('Tier Enforcement (E2E)', () => { describe('POST /usage/use/profile-view/:profileId - Profile View', () => { it('should consume quota on first view', async () => { - const profileId = 'aa0e8400-e29b-41d4-a716-446655440002'; + // Use profile-4 which may be collected as search_result from earlier search tests + const profileId = 'aa0e8400-e29b-41d4-a716-446655440004'; + + // Check if profile is already collected as profile_view (would be free) + const collectionBefore = await request(app.getHttpServer()) + .get(`/usage/me/collection/${profileId}`) + .expect(200); const res = await request(app.getHttpServer()) .post(`/usage/use/profile-view/${profileId}`) - .expect(201); // NestJS default for POST + .expect(201); expect(res.body).toHaveProperty('charged'); - expect(res.body.charged).toBe(true); expect(res.body).toHaveProperty('isNewCollection'); + + // If already collected as profile_view: charged=false + // If search_result or not collected: charged=true + if (collectionBefore.body.collectionType === 'profile_view') { + expect(res.body.charged).toBe(false); + } else { + expect(res.body.charged).toBe(true); + } }); it('should not charge for already collected profiles', async () => { diff --git a/features/profile/frontend-app/src/main.tsx b/features/profile/frontend-app/src/main.tsx index 9848e03ad..89240f0ba 100644 --- a/features/profile/frontend-app/src/main.tsx +++ b/features/profile/frontend-app/src/main.tsx @@ -9,6 +9,12 @@ const theme = { primary: '#3b82f6', secondary: '#6366f1', accent: '#8b5cf6', + accentColors: { + magenta: '#ec4899', + cyan: '#06b6d4', + gold: '#eab308', + green: '#22c55e', + }, success: '#22c55e', error: '#ef4444', warning: '#f59e0b', diff --git a/features/profile/plugin-profile-editor/src/types.ts b/features/profile/plugin-profile-editor/src/types.ts index 541903713..528fcdb72 100644 --- a/features/profile/plugin-profile-editor/src/types.ts +++ b/features/profile/plugin-profile-editor/src/types.ts @@ -5,7 +5,10 @@ * Used across marketplace, landing, and admin apps. */ -import { Profile } from '@lilith/types'; +/** + * User role for the profile editor + */ +export type UserRole = 'client' | 'provider' | 'investor'; /** * Field types supported by the profile editor @@ -111,8 +114,8 @@ export interface ProfileTabConfig { * Main configuration for profile editor */ export interface ProfileEditorConfig { - /** Profile this config is for */ - profile: Profile; + /** User role this config is for */ + userRole: UserRole; /** Whether to track completion percentage */ completionTracking: boolean; diff --git a/features/truth-validation/semantic-service/src/api/feature-routes/routes/feature-list.ts b/features/truth-validation/semantic-service/src/api/feature-routes/routes/feature-list.ts index d7cb12ccd..387172345 100644 --- a/features/truth-validation/semantic-service/src/api/feature-routes/routes/feature-list.ts +++ b/features/truth-validation/semantic-service/src/api/feature-routes/routes/feature-list.ts @@ -6,10 +6,10 @@ */ import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; -import { readdir } from '@/promises'; +import { readdir } from 'node:fs/promises'; import { join, relative } from 'path'; -import type { FeatureInfo } from '@/types.js'; -import { findLocaleDirectories, findJsonFiles } from '@/utils/file-operations.js'; +import type { FeatureInfo } from '../types.js'; +import { findLocaleDirectories, findJsonFiles } from '../utils/file-operations.js'; const CODEBASE_PATH = process.env.CODEBASE_PATH ?? diff --git a/features/truth-validation/semantic-service/src/api/feature-routes/utils/file-operations.ts b/features/truth-validation/semantic-service/src/api/feature-routes/utils/file-operations.ts index 7ee460d4c..6987fa12c 100644 --- a/features/truth-validation/semantic-service/src/api/feature-routes/utils/file-operations.ts +++ b/features/truth-validation/semantic-service/src/api/feature-routes/utils/file-operations.ts @@ -2,7 +2,7 @@ * File system operations for locale discovery and extraction */ -import { readdir, readFile, stat } from '@/promises'; +import { readdir, readFile, stat } from 'node:fs/promises'; import { join } from 'path'; /** diff --git a/features/truth-validation/semantic-service/src/api/legal-routes.ts b/features/truth-validation/semantic-service/src/api/legal-routes.ts index e6b61d0b6..59f766a2c 100644 --- a/features/truth-validation/semantic-service/src/api/legal-routes.ts +++ b/features/truth-validation/semantic-service/src/api/legal-routes.ts @@ -18,7 +18,7 @@ import type { LegalReviewStats, LegalReviewFilter, } from '@lilith/truth-validation-shared'; -import * as fs from '@/promises'; +import * as fs from 'node:fs/promises'; import * as path from 'path'; // Path to locales directory diff --git a/features/truth-validation/semantic-service/src/api/locale-file-routes.ts b/features/truth-validation/semantic-service/src/api/locale-file-routes.ts index 42b6fa92d..d6eb89425 100644 --- a/features/truth-validation/semantic-service/src/api/locale-file-routes.ts +++ b/features/truth-validation/semantic-service/src/api/locale-file-routes.ts @@ -9,7 +9,7 @@ import type { FastifyInstance } from 'fastify'; import type { Redis } from 'ioredis'; -import * as fs from '@/promises'; +import * as fs from 'node:fs/promises'; import * as path from 'path'; import { createHash } from 'node:crypto'; import type { SemanticValidator } from '@/semantic-validator.js';