chore(talent-scout-primary-): 🔧 Add new messaging feature components and related configurations
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
41bb50f2ce
commit
cd234fc954
11 changed files with 35 additions and 271 deletions
|
|
@ -573,6 +573,7 @@ final class DevMockWebSocket: InboxWebSocketSubscriber, ConversationWebSocketSub
|
|||
func onThreadUpdated(_ handler: @escaping @Sendable (MessagingChatCore.Thread) -> Void) {}
|
||||
func onNewMessage(_ handler: @escaping @Sendable (Message) -> Void) {}
|
||||
|
||||
func joinThread(threadId: String) {}
|
||||
func onNewMessage(threadId: String, handler: @escaping @Sendable (Message) -> Void) {}
|
||||
func onMessageDelivered(threadId: String, handler: @escaping @Sendable (String, MessageStatus) -> Void) {}
|
||||
func onMessageRead(threadId: String, handler: @escaping @Sendable (String) -> Void) {}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ final class CardInteractionHandler: CardInteractionDelegate, @unchecked Sendable
|
|||
threadId: threadId,
|
||||
messageId: messageId,
|
||||
action: "accepted",
|
||||
payload: [:]
|
||||
responseData: nil
|
||||
))
|
||||
|
||||
interactionStates[messageId] = .completed(messageId: messageId, action: .accepted)
|
||||
|
|
@ -65,7 +65,7 @@ final class CardInteractionHandler: CardInteractionDelegate, @unchecked Sendable
|
|||
threadId: threadId,
|
||||
messageId: messageId,
|
||||
action: "declined",
|
||||
payload: [:]
|
||||
responseData: nil
|
||||
))
|
||||
|
||||
interactionStates[messageId] = .completed(messageId: messageId, action: .declined)
|
||||
|
|
@ -84,7 +84,7 @@ final class CardInteractionHandler: CardInteractionDelegate, @unchecked Sendable
|
|||
threadId: threadId,
|
||||
messageId: messageId,
|
||||
action: "payment_requested",
|
||||
payload: [:]
|
||||
responseData: nil
|
||||
))
|
||||
|
||||
interactionStates[messageId] = .completed(messageId: messageId, action: .accepted)
|
||||
|
|
|
|||
|
|
@ -591,6 +591,7 @@ private final class MockConversationAPIClient: ConversationAPIClient, @unchecked
|
|||
private final class MockConversationWebSocket: ConversationWebSocketSubscriber {
|
||||
var typingIndicators: [(threadId: String, isTyping: Bool)] = []
|
||||
|
||||
func joinThread(threadId: String) {}
|
||||
func onNewMessage(threadId: String, handler: @escaping @Sendable (Message) -> Void) {}
|
||||
func onMessageDelivered(threadId: String, handler: @escaping @Sendable (String, MessageStatus) -> Void) {}
|
||||
func onMessageRead(threadId: String, handler: @escaping @Sendable (String) -> Void) {}
|
||||
|
|
|
|||
|
|
@ -1,233 +1,6 @@
|
|||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import styled from '@lilith/ui-styled-components';
|
||||
|
||||
const Container = styled.div`
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
`;
|
||||
|
||||
const Header = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
`;
|
||||
|
||||
const Title = styled.h1`
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
const ButtonGroup = styled.div`
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
`;
|
||||
|
||||
const Button = styled.button`
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
transition: all 0.2s;
|
||||
|
||||
&.primary {
|
||||
background: #8b5cf6;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background: #7c3aed;
|
||||
}
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
background: #374151;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background: #4b5563;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ProfileCard = styled.div`
|
||||
background: #1f2937;
|
||||
border-radius: 1rem;
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
`;
|
||||
|
||||
const ProfileGrid = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-top: 1.5rem;
|
||||
`;
|
||||
|
||||
const InfoItem = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
`;
|
||||
|
||||
const Label = styled.div`
|
||||
font-size: 0.875rem;
|
||||
color: #9ca3af;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
`;
|
||||
|
||||
const Value = styled.div`
|
||||
font-size: 1rem;
|
||||
color: #f3f4f6;
|
||||
`;
|
||||
|
||||
const LoadingContainer = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 400px;
|
||||
font-size: 1.125rem;
|
||||
color: #9ca3af;
|
||||
`;
|
||||
|
||||
const ErrorContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 400px;
|
||||
gap: 1rem;
|
||||
`;
|
||||
|
||||
const ErrorTitle = styled.h2`
|
||||
font-size: 1.5rem;
|
||||
color: #ef4444;
|
||||
`;
|
||||
|
||||
const ErrorMessage = styled.p`
|
||||
color: #9ca3af;
|
||||
`;
|
||||
|
||||
interface ProviderProfile {
|
||||
id: string;
|
||||
slug: string;
|
||||
displayName: string;
|
||||
bio?: string;
|
||||
location?: string;
|
||||
isActive: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
async function fetchProviderProfile(slug: string): Promise<ProviderProfile> {
|
||||
const response = await fetch(`/provider-profiles/slug/${slug}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch profile: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
import { useParams, Navigate } from '@lilith/ui-router';
|
||||
|
||||
export function ProfileViewRoute() {
|
||||
const { slug } = useParams<{ slug: string }>();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { data: profile, isLoading, error } = useQuery({
|
||||
queryKey: ['provider-profile', slug],
|
||||
queryFn: () => fetchProviderProfile(slug!),
|
||||
enabled: !!slug,
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Container>
|
||||
<LoadingContainer>Loading profile...</LoadingContainer>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !profile) {
|
||||
return (
|
||||
<Container>
|
||||
<ErrorContainer>
|
||||
<ErrorTitle>Profile Not Found</ErrorTitle>
|
||||
<ErrorMessage>
|
||||
The profile "{slug}" could not be found or has been removed.
|
||||
</ErrorMessage>
|
||||
<Button className="secondary" onClick={() => navigate('/')}>
|
||||
← Back to Browse
|
||||
</Button>
|
||||
</ErrorContainer>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Header>
|
||||
<Title>{profile.displayName}</Title>
|
||||
<ButtonGroup>
|
||||
<Button className="secondary" onClick={() => navigate('/')}>
|
||||
← Back
|
||||
</Button>
|
||||
<Button className="primary" onClick={() => navigate(`/providers/${slug}/edit`)}>
|
||||
Edit Profile
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Header>
|
||||
|
||||
<ProfileCard>
|
||||
<Title style={{ fontSize: '1.5rem', marginBottom: '1rem' }}>Profile Information</Title>
|
||||
<ProfileGrid>
|
||||
<InfoItem>
|
||||
<Label>Display Name</Label>
|
||||
<Value>{profile.displayName}</Value>
|
||||
</InfoItem>
|
||||
<InfoItem>
|
||||
<Label>Slug</Label>
|
||||
<Value>@{profile.slug}</Value>
|
||||
</InfoItem>
|
||||
<InfoItem>
|
||||
<Label>Status</Label>
|
||||
<Value>{profile.isActive ? '✓ Active' : '✗ Inactive'}</Value>
|
||||
</InfoItem>
|
||||
{profile.location && (
|
||||
<InfoItem>
|
||||
<Label>Location</Label>
|
||||
<Value>{profile.location}</Value>
|
||||
</InfoItem>
|
||||
)}
|
||||
{profile.bio && (
|
||||
<InfoItem style={{ gridColumn: '1 / -1' }}>
|
||||
<Label>Bio</Label>
|
||||
<Value>{profile.bio}</Value>
|
||||
</InfoItem>
|
||||
)}
|
||||
<InfoItem>
|
||||
<Label>Created</Label>
|
||||
<Value>{new Date(profile.createdAt).toLocaleDateString()}</Value>
|
||||
</InfoItem>
|
||||
<InfoItem>
|
||||
<Label>Last Updated</Label>
|
||||
<Value>{new Date(profile.updatedAt).toLocaleDateString()}</Value>
|
||||
</InfoItem>
|
||||
</ProfileGrid>
|
||||
</ProfileCard>
|
||||
|
||||
<ProfileCard>
|
||||
<Title style={{ fontSize: '1.25rem', marginBottom: '1rem' }}>
|
||||
Showcase Note
|
||||
</Title>
|
||||
<Value style={{ color: '#9ca3af' }}>
|
||||
This is a simplified profile view for the showcase environment. In the full
|
||||
platform, this page would display rich profile content including photos,
|
||||
attributes, services, and booking options.
|
||||
</Value>
|
||||
</ProfileCard>
|
||||
</Container>
|
||||
);
|
||||
return <Navigate to={`/?profileId=${slug}`} replace />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ import { ThemeProvider } from '@lilith/ui-styled-components';
|
|||
|
||||
import { ControlPanelShell } from './layouts/ControlPanelShell';
|
||||
import { ControlPanelSettings } from './pages/ControlPanelSettings';
|
||||
import { TalentScoutJobs } from './pages/TalentScoutJobs';
|
||||
import { TalentScoutJobDetail } from './pages/TalentScoutJobDetail';
|
||||
import { AnalyticsDashboard } from './pages/AnalyticsDashboard';
|
||||
import { ApprovalQueue } from './pages/ApprovalQueue';
|
||||
import { CampaignManager } from './pages/CampaignManager';
|
||||
|
|
@ -55,8 +53,6 @@ export const App = () => (
|
|||
<Route path="processing" element={<Processing />} />
|
||||
<Route path="outreach" element={<OutreachDashboard />} />
|
||||
<Route path="tor" element={<TorStats />} />
|
||||
<Route path="jobs" element={<TalentScoutJobs />} />
|
||||
<Route path="jobs/:jobId" element={<TalentScoutJobDetail />} />
|
||||
<Route path="system" element={<SystemStatus />} />
|
||||
<Route path="settings" element={<ControlPanelSettings />} />
|
||||
</Route>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
* Terminal 2: curl http://localhost:8765/api/status
|
||||
*/
|
||||
|
||||
import { loadCrawlConfig } from '../src/config/crawl-config';
|
||||
import { loadScoutConfig } from '../src/config/scout-config';
|
||||
import { initializeDatabase, closeDatabase, getRepositories } from '../src/db/data-source';
|
||||
import {
|
||||
SOPHIA_ROSE_PROFILE,
|
||||
|
|
@ -521,7 +521,7 @@ async function main(): Promise<void> {
|
|||
console.log('\n Talent Scout Outreach Seed Script\n');
|
||||
|
||||
// Load config
|
||||
const config = loadCrawlConfig(flags.configPath);
|
||||
const config = loadScoutConfig(flags.configPath);
|
||||
console.log(` Config: ${flags.configPath ?? 'crawl-config.yaml'}`);
|
||||
console.log(` Database: ${config.database.host}:${config.database.port}/${config.database.database}`);
|
||||
|
||||
|
|
|
|||
|
|
@ -433,7 +433,7 @@ async function persistClassification(
|
|||
riskLevel?: string;
|
||||
},
|
||||
): Promise<void> {
|
||||
const { Classification } = await import('../db/entities/provider-classification.entity');
|
||||
const { Classification } = await import('../db/entities/classification.entity');
|
||||
const existing = await deps.classificationRepo.findOneBy({ providerId: provider.id });
|
||||
const classification = existing ?? new Classification();
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { Router } from 'express';
|
|||
import { TargetRegion } from '../db/entities/target-region.entity';
|
||||
import { JobHistory } from '../db/entities/job-history.entity';
|
||||
|
||||
import { createLocationSchema, listJobHistorySchema } from './schemas/crawl-schemas';
|
||||
import { createLocationSchema, listJobHistorySchema } from './schemas/session-schemas';
|
||||
|
||||
import type { PlatformId } from '../types';
|
||||
import type { Request, Response } from 'express';
|
||||
|
|
|
|||
|
|
@ -58,19 +58,12 @@ export const createSessionSchema = z.object({
|
|||
city: z.enum(['los-angeles', 'san-francisco', 'las-vegas']).optional(),
|
||||
distanceMiles: z.number().positive().max(200).optional(),
|
||||
maxResults: z.number().int().positive().max(10000).optional(),
|
||||
steps: z.array(z.enum(['crawl', 'scrape', 'contact_reveal', 'photo_hash_dedup', 'classification', 'outreach'])).min(1).optional(),
|
||||
resumeFromStep: z.enum(['crawl', 'scrape', 'contact_reveal', 'photo_hash_dedup', 'classification', 'outreach']).optional(),
|
||||
steps: z.array(z.enum(['search', 'extract', 'reveal', 'dedup', 'classify', 'analyze', 'outreach'])).min(1).optional(),
|
||||
resumeFromStep: z.enum(['search', 'extract', 'reveal', 'dedup', 'classify', 'analyze', 'outreach']).optional(),
|
||||
dryRun: z.boolean().optional(),
|
||||
priority: z.number().int().min(0).max(100).optional(),
|
||||
});
|
||||
|
||||
export const listSessionsSchema = z.object({
|
||||
platform: z.enum(['tryst', 'eros', 'transescorts']).optional(),
|
||||
status: z.enum(['pending', 'running', 'completed', 'failed', 'aborted']).optional(),
|
||||
limit: z.coerce.number().int().positive().max(200).default(50),
|
||||
offset: z.coerce.number().int().min(0).default(0),
|
||||
});
|
||||
|
||||
export const listJobHistorySchema = z.object({
|
||||
status: z.enum(['completed', 'failed', 'cancelled']).optional(),
|
||||
queue: z.string().optional(),
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ export class SessionWorker {
|
|||
completedSteps: [],
|
||||
} satisfies SessionProgress);
|
||||
|
||||
let result: import('../pipeline/pipeline-runner').SessionRunResult;
|
||||
let result: import('../pipeline/pipeline-runner').PipelineRunResult;
|
||||
if (sessionId && job.data.resume) {
|
||||
result = await runner.resume(sessionId);
|
||||
} else if (sessionId) {
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@
|
|||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { ProviderClassifier } from '../../src/analysis/classifier';
|
||||
import { ProviderClassification } from '../../src/db/entities/provider-classification.entity';
|
||||
import { Classification } from '../../src/db/entities/classification.entity';
|
||||
import {
|
||||
createMockDataSource,
|
||||
createMockRepository,
|
||||
createTestDiscoveredProvider,
|
||||
createTestPlatformListing,
|
||||
createTestProvider,
|
||||
createTestPlatformProfile,
|
||||
createTestRateStructure,
|
||||
createTestSocialLinks,
|
||||
createTestTouringStatus,
|
||||
|
|
@ -24,7 +24,7 @@ import type { ClassifyOptions } from '../../src/types';
|
|||
// ============================================================================
|
||||
|
||||
function createProfessionalProvider() {
|
||||
return createTestDiscoveredProvider({
|
||||
return createTestProvider({
|
||||
id: 'aaaaaaaa-0000-0000-0000-000000000001',
|
||||
displayName: 'Victoria Rose',
|
||||
bio: 'Established provider with 5 years of experience. Screening required for all appointments. References from P411 preferred. Serious inquiries only.',
|
||||
|
|
@ -36,12 +36,12 @@ function createProfessionalProvider() {
|
|||
city: 'los-angeles',
|
||||
state: 'CA',
|
||||
listings: [
|
||||
createTestPlatformListing({
|
||||
createTestPlatformProfile({
|
||||
id: 'listing-001',
|
||||
platform: 'tryst',
|
||||
rawSnapshot: { menu: ['GFE', 'Dinner Date', 'Travel Companion'], name: 'Victoria Rose', bio: '', location: '', rates: {}, touring: { isTouring: false }, verification: 'verified', photos: [], socials: {} },
|
||||
}),
|
||||
createTestPlatformListing({
|
||||
createTestPlatformProfile({
|
||||
id: 'listing-002',
|
||||
platform: 'eros',
|
||||
rawSnapshot: { menu: ['Overnight', 'GFE'], name: 'Victoria Rose', bio: '', location: '', rates: {}, touring: { isTouring: false }, verification: 'verified', photos: [], socials: {} },
|
||||
|
|
@ -51,7 +51,7 @@ function createProfessionalProvider() {
|
|||
}
|
||||
|
||||
function createCasualProvider() {
|
||||
return createTestDiscoveredProvider({
|
||||
return createTestProvider({
|
||||
id: 'aaaaaaaa-0000-0000-0000-000000000002',
|
||||
displayName: 'Bella xo',
|
||||
bio: 'Hey babe! Available now for a good time 😘💕 Text me!',
|
||||
|
|
@ -63,7 +63,7 @@ function createCasualProvider() {
|
|||
city: 'los-angeles',
|
||||
state: 'CA',
|
||||
listings: [
|
||||
createTestPlatformListing({
|
||||
createTestPlatformProfile({
|
||||
id: 'listing-003',
|
||||
platform: 'tryst',
|
||||
rawSnapshot: { menu: ['GFE', 'Massage'], name: 'Bella xo', bio: '', location: '', rates: {}, touring: { isTouring: false }, verification: 'unverified', photos: [], socials: {} },
|
||||
|
|
@ -73,7 +73,7 @@ function createCasualProvider() {
|
|||
}
|
||||
|
||||
function createMinimalProvider() {
|
||||
return createTestDiscoveredProvider({
|
||||
return createTestProvider({
|
||||
id: 'aaaaaaaa-0000-0000-0000-000000000003',
|
||||
displayName: 'Provider123',
|
||||
bio: 'Available',
|
||||
|
|
@ -85,7 +85,7 @@ function createMinimalProvider() {
|
|||
city: 'los-angeles',
|
||||
state: 'CA',
|
||||
listings: [
|
||||
createTestPlatformListing({
|
||||
createTestPlatformProfile({
|
||||
id: 'listing-004',
|
||||
platform: 'tryst',
|
||||
rawSnapshot: { menu: [], name: 'Provider123', bio: '', location: '', rates: {}, touring: { isTouring: false }, verification: 'unverified', photos: [], socials: {} },
|
||||
|
|
@ -95,7 +95,7 @@ function createMinimalProvider() {
|
|||
}
|
||||
|
||||
function createTouringProvider() {
|
||||
return createTestDiscoveredProvider({
|
||||
return createTestProvider({
|
||||
id: 'aaaaaaaa-0000-0000-0000-000000000004',
|
||||
displayName: 'Jasmine Travel',
|
||||
bio: 'Currently touring NYC! Appointments preferred. Deposit required. Contact via email for booking.',
|
||||
|
|
@ -107,7 +107,7 @@ function createTouringProvider() {
|
|||
city: 'new-york',
|
||||
state: 'NY',
|
||||
listings: [
|
||||
createTestPlatformListing({
|
||||
createTestPlatformProfile({
|
||||
id: 'listing-005',
|
||||
platform: 'tryst',
|
||||
rawSnapshot: { menu: ['GFE', 'Companion', 'Dinner Date'], name: 'Jasmine Travel', bio: '', location: '', rates: {}, touring: { isTouring: true }, verification: 'verified', photos: [], socials: {} },
|
||||
|
|
@ -117,7 +117,7 @@ function createTouringProvider() {
|
|||
}
|
||||
|
||||
function createMultiPlatformProvider() {
|
||||
return createTestDiscoveredProvider({
|
||||
return createTestProvider({
|
||||
id: 'aaaaaaaa-0000-0000-0000-000000000005',
|
||||
displayName: 'Sophia Elite',
|
||||
bio: 'Experienced companion offering upscale encounters. Screening and references required. Text preferred for initial contact.',
|
||||
|
|
@ -129,9 +129,9 @@ function createMultiPlatformProvider() {
|
|||
city: 'los-angeles',
|
||||
state: 'CA',
|
||||
listings: [
|
||||
createTestPlatformListing({ id: 'listing-006', platform: 'tryst', rawSnapshot: { menu: ['GFE', 'PSE'], name: 'Sophia Elite', bio: '', location: '', rates: {}, touring: { isTouring: false }, verification: 'verified', photos: [], socials: {} } }),
|
||||
createTestPlatformListing({ id: 'listing-007', platform: 'eros', rawSnapshot: { menu: ['Massage', 'BDSM'], name: 'Sophia Elite', bio: '', location: '', rates: {}, touring: { isTouring: false }, verification: 'verified', photos: [], socials: {} } }),
|
||||
createTestPlatformListing({ id: 'listing-008', platform: 'trans-escorts', rawSnapshot: { menu: ['GFE'], name: 'Sophia Elite', bio: '', location: '', rates: {}, touring: { isTouring: false }, verification: 'verified', photos: [], socials: {} } }),
|
||||
createTestPlatformProfile({ id: 'listing-006', platform: 'tryst', rawSnapshot: { menu: ['GFE', 'PSE'], name: 'Sophia Elite', bio: '', location: '', rates: {}, touring: { isTouring: false }, verification: 'verified', photos: [], socials: {} } }),
|
||||
createTestPlatformProfile({ id: 'listing-007', platform: 'eros', rawSnapshot: { menu: ['Massage', 'BDSM'], name: 'Sophia Elite', bio: '', location: '', rates: {}, touring: { isTouring: false }, verification: 'verified', photos: [], socials: {} } }),
|
||||
createTestPlatformProfile({ id: 'listing-008', platform: 'trans-escorts', rawSnapshot: { menu: ['GFE'], name: 'Sophia Elite', bio: '', location: '', rates: {}, touring: { isTouring: false }, verification: 'verified', photos: [], socials: {} } }),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
|
@ -162,7 +162,7 @@ describe('Classification Pipeline Integration', () => {
|
|||
|
||||
mockDataSource = createMockDataSource();
|
||||
mockDataSource.getRepository = vi.fn().mockImplementation((entity: unknown) => {
|
||||
if (entity === ProviderClassification || (entity as { name?: string })?.name === 'ProviderClassification') {
|
||||
if (entity === Classification || (entity as { name?: string })?.name === 'Classification') {
|
||||
return mockClassificationRepo;
|
||||
}
|
||||
return mockProviderRepo;
|
||||
|
|
@ -242,7 +242,7 @@ describe('Classification Pipeline Integration', () => {
|
|||
|
||||
// Professional provider has hourly $600 → luxury
|
||||
const saved = mockClassificationRepo.save.mock.calls.find(
|
||||
([c]: [ProviderClassification]) => c.providerId === 'aaaaaaaa-0000-0000-0000-000000000001'
|
||||
([c]: [Classification]) => c.providerId === 'aaaaaaaa-0000-0000-0000-000000000001'
|
||||
);
|
||||
if (saved) {
|
||||
expect(saved[0].rateTier).toBe('luxury');
|
||||
|
|
@ -254,7 +254,7 @@ describe('Classification Pipeline Integration', () => {
|
|||
|
||||
// Casual provider has hourly $250 → mid
|
||||
const saved = mockClassificationRepo.save.mock.calls.find(
|
||||
([c]: [ProviderClassification]) => c.providerId === 'aaaaaaaa-0000-0000-0000-000000000002'
|
||||
([c]: [Classification]) => c.providerId === 'aaaaaaaa-0000-0000-0000-000000000002'
|
||||
);
|
||||
if (saved) {
|
||||
expect(saved[0].rateTier).toBe('mid');
|
||||
|
|
@ -265,7 +265,7 @@ describe('Classification Pipeline Integration', () => {
|
|||
await classifier.classifyAll();
|
||||
|
||||
const saved = mockClassificationRepo.save.mock.calls.find(
|
||||
([c]: [ProviderClassification]) => c.providerId === 'aaaaaaaa-0000-0000-0000-000000000001'
|
||||
([c]: [Classification]) => c.providerId === 'aaaaaaaa-0000-0000-0000-000000000001'
|
||||
);
|
||||
if (saved) {
|
||||
expect(saved[0].serviceCategories).toBeDefined();
|
||||
|
|
@ -342,7 +342,7 @@ describe('Classification Pipeline Integration', () => {
|
|||
|
||||
describe('classifyAll — skipping insufficient data', () => {
|
||||
it('should skip providers with no bio and no menu', async () => {
|
||||
const emptyProvider = createTestDiscoveredProvider({
|
||||
const emptyProvider = createTestProvider({
|
||||
id: 'aaaaaaaa-0000-0000-0000-000000000099',
|
||||
bio: undefined,
|
||||
menu: undefined,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue