fix(main): 🐛 resolve package.json dependencies conflicts
This commit is contained in:
parent
c5bababc19
commit
ca0f4e77f4
13 changed files with 318 additions and 118 deletions
|
|
@ -22,6 +22,7 @@
|
|||
"queue:control": "queue-control -q subscription-renewals"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lilith/domain-events": "workspace:*",
|
||||
"@lilith/geo-utils": "^1.2.0",
|
||||
"@lilith/service-nestjs-bootstrap": "^1.0.0",
|
||||
"@lilith/marketplace-shared": "workspace:*",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import {
|
|||
Post,
|
||||
Body,
|
||||
Param,
|
||||
Headers,
|
||||
ParseUUIDPipe,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
|
|
@ -151,12 +152,14 @@ export class SubscriptionsController {
|
|||
@ApiResponse({ status: 404, description: 'Subscription not found' })
|
||||
async complete3DS(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Headers('x-analytics-session') analyticsSessionId: string | undefined,
|
||||
@Body() body: { externalId: string; transactionId?: string },
|
||||
): Promise<PlatformSubscription> {
|
||||
return this.subscriptionsService.activate(
|
||||
id,
|
||||
body.externalId,
|
||||
body.transactionId,
|
||||
analyticsSessionId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Module, forwardRef } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { DomainEventsModule } from '@lilith/domain-events';
|
||||
|
||||
import { SubscriptionsController } from './subscriptions.controller';
|
||||
import { SubscriptionsService } from './subscriptions.service';
|
||||
|
|
@ -19,6 +20,7 @@ import { UsageModule } from '../usage/usage.module';
|
|||
PlatformSubscriptionTier,
|
||||
SubscriptionTierChange,
|
||||
]),
|
||||
DomainEventsModule.forFeature(),
|
||||
TiersModule,
|
||||
BillingModule,
|
||||
UsageModule, // For AnalyticsEventsService - subscription lifecycle tracking
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, IsNull, Not } from 'typeorm';
|
||||
import { DomainEventsEmitter } from '@lilith/domain-events';
|
||||
|
||||
import {
|
||||
PlatformSubscription,
|
||||
|
|
@ -39,6 +40,7 @@ export class SubscriptionsService {
|
|||
private readonly billingService: BillingService,
|
||||
private readonly prorationService: ProrationService,
|
||||
private readonly analyticsEvents: AnalyticsEventsService,
|
||||
private readonly domainEvents: DomainEventsEmitter,
|
||||
) {}
|
||||
|
||||
/**
|
||||
|
|
@ -149,6 +151,7 @@ export class SubscriptionsService {
|
|||
subscriptionId: string,
|
||||
externalId: string,
|
||||
transactionId?: string,
|
||||
analyticsSessionId?: string,
|
||||
): Promise<PlatformSubscription> {
|
||||
const subscription = await this.findById(subscriptionId);
|
||||
|
||||
|
|
@ -164,6 +167,20 @@ export class SubscriptionsService {
|
|||
await this.subscriptionRepo.save(subscription);
|
||||
this.logger.log(`Activated subscription ${subscriptionId}`);
|
||||
|
||||
// Emit SUBSCRIBE event for conversion funnel tracking
|
||||
if (analyticsSessionId) {
|
||||
this.domainEvents
|
||||
.emitSubscribe({
|
||||
sessionId: analyticsSessionId,
|
||||
userId: subscription.userId,
|
||||
subscriptionId: subscription.id,
|
||||
tier: subscription.tier?.slug || 'unknown',
|
||||
priceInCents: Math.round(Number(subscription.tier?.priceUsd || 0) * 100),
|
||||
attribution: this.domainEvents.createEmptyAttribution(),
|
||||
})
|
||||
.catch((err: Error) => this.logger.warn(`Failed to emit subscribe event: ${err.message}`));
|
||||
}
|
||||
|
||||
return subscription;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,12 +20,15 @@
|
|||
"test:cov": "vitest run --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lilith/domain-events": "workspace:*",
|
||||
"@nestjs/axios": "^4.0.1",
|
||||
"@nestjs/bullmq": "^11.0.4",
|
||||
"@nestjs/common": "^11.1.11",
|
||||
"@nestjs/config": "^4.0.2",
|
||||
"@nestjs/core": "^11.1.11",
|
||||
"@nestjs/platform-express": "^11.1.11",
|
||||
"axios": "^1.6.0",
|
||||
"bullmq": "^5.34.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { Module } from '@nestjs/common'
|
||||
import { ConfigModule } from '@nestjs/config'
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config'
|
||||
import { BullModule } from '@nestjs/bullmq'
|
||||
import { DomainEventsModule } from '@lilith/domain-events'
|
||||
|
||||
import { GiftCardsModule } from './gift-cards/gift-cards.module'
|
||||
import { SegpayModule } from './segpay/segpay.module'
|
||||
|
|
@ -16,6 +18,7 @@ import { WebhooksModule } from './webhooks/webhooks.module'
|
|||
* - Payment provider factory for provider selection
|
||||
* - Webhook handling
|
||||
* - Gift card purchases
|
||||
* - Domain events for funnel tracking
|
||||
*/
|
||||
@Module({
|
||||
imports: [
|
||||
|
|
@ -23,6 +26,19 @@ import { WebhooksModule } from './webhooks/webhooks.module'
|
|||
isGlobal: true,
|
||||
envFilePath: ['.env.local', '.env'],
|
||||
}),
|
||||
// Queue infrastructure for domain events
|
||||
BullModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
useFactory: (configService: ConfigService) => ({
|
||||
connection: {
|
||||
host: configService.get('REDIS_HOST', 'localhost'),
|
||||
port: configService.get<number>('REDIS_PORT', 6379),
|
||||
password: configService.get('REDIS_PASSWORD'),
|
||||
},
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
DomainEventsModule.forFeature(),
|
||||
SegpayModule,
|
||||
NOWPaymentsModule,
|
||||
ProvidersModule,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
Req,
|
||||
} from '@nestjs/common'
|
||||
import { Request } from 'express'
|
||||
import { DomainEventsEmitter } from '@lilith/domain-events'
|
||||
|
||||
import { SegpayProvider } from '../segpay/segpay.provider'
|
||||
import { PaymentAnalyticsService, TransactionType } from '../services/payment-analytics.service'
|
||||
|
|
@ -36,6 +37,7 @@ export class SegpayWebhookController {
|
|||
constructor(
|
||||
private readonly segpayProvider: SegpayProvider,
|
||||
private readonly paymentAnalytics: PaymentAnalyticsService,
|
||||
private readonly domainEvents: DomainEventsEmitter,
|
||||
) {}
|
||||
|
||||
@Post()
|
||||
|
|
@ -143,17 +145,34 @@ export class SegpayWebhookController {
|
|||
private async handleSubscriptionCreated(data: Record<string, unknown>): Promise<void> {
|
||||
this.logger.log(`Subscription created: ${data.subscriptionId}, user: ${data.userId}`)
|
||||
|
||||
const userId = data.userId as string
|
||||
const amount = Number(data.amount || 0)
|
||||
const transactionId = (data.subscriptionId as string) || `sub_${Date.now()}`
|
||||
|
||||
// Track revenue for analytics
|
||||
if (data.userId && data.amount) {
|
||||
if (userId && amount) {
|
||||
await this.paymentAnalytics.trackRevenue({
|
||||
userId: data.userId as string,
|
||||
transactionId: (data.subscriptionId as string) || `sub_${Date.now()}`,
|
||||
userId,
|
||||
transactionId,
|
||||
transactionType: TransactionType.SUBSCRIPTION,
|
||||
amount: Number(data.amount),
|
||||
amount,
|
||||
currency: (data.currency as string) || 'USD',
|
||||
platformFee: Number(data.platformFee || 0),
|
||||
metadata: 'subscription.created',
|
||||
})
|
||||
|
||||
// Emit PURCHASE event for conversion funnel (first subscription payment)
|
||||
// Uses transactionId as correlationId since we don't have analytics session
|
||||
this.domainEvents
|
||||
.emitPurchase({
|
||||
sessionId: transactionId, // Use transactionId as correlation key
|
||||
userId,
|
||||
transactionId,
|
||||
amountInCents: Math.round(amount * 100),
|
||||
type: 'subscription',
|
||||
attribution: this.domainEvents.createEmptyAttribution(),
|
||||
})
|
||||
.catch((err: Error) => this.logger.warn(`Failed to emit purchase event: ${err.message}`))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -165,42 +184,81 @@ export class SegpayWebhookController {
|
|||
private async handleSubscriptionRenewed(data: Record<string, unknown>): Promise<void> {
|
||||
this.logger.log(`Subscription renewed: ${data.subscriptionId}`)
|
||||
|
||||
const userId = data.userId as string
|
||||
const amount = Number(data.amount || 0)
|
||||
const transactionId = (data.transactionId as string) || `renewal_${Date.now()}`
|
||||
|
||||
// Track renewal revenue
|
||||
if (data.userId && data.amount) {
|
||||
if (userId && amount) {
|
||||
await this.paymentAnalytics.trackRevenue({
|
||||
userId: data.userId as string,
|
||||
transactionId: (data.transactionId as string) || `renewal_${Date.now()}`,
|
||||
userId,
|
||||
transactionId,
|
||||
transactionType: TransactionType.SUBSCRIPTION,
|
||||
amount: Number(data.amount),
|
||||
amount,
|
||||
currency: (data.currency as string) || 'USD',
|
||||
platformFee: Number(data.platformFee || 0),
|
||||
metadata: 'subscription.renewed',
|
||||
})
|
||||
|
||||
// Emit REPEAT_PURCHASE event for conversion funnel (subscription renewals are repeat)
|
||||
const purchaseCount = Number(data.renewalCount || 2) // Renewals are at least 2nd payment
|
||||
this.domainEvents
|
||||
.emitRepeatPurchase({
|
||||
sessionId: transactionId,
|
||||
userId,
|
||||
transactionId,
|
||||
amountInCents: Math.round(amount * 100),
|
||||
purchaseCount,
|
||||
attribution: this.domainEvents.createEmptyAttribution(),
|
||||
})
|
||||
.catch((err: Error) => this.logger.warn(`Failed to emit repeat purchase event: ${err.message}`))
|
||||
}
|
||||
}
|
||||
|
||||
private async handlePaymentSucceeded(data: Record<string, unknown>): Promise<void> {
|
||||
this.logger.log(`Payment succeeded: ${data.transactionId}, amount: $${data.amount}`)
|
||||
|
||||
const userId = data.userId as string
|
||||
const amount = Number(data.amount || 0)
|
||||
const transactionId = data.transactionId as string
|
||||
const paymentType = data.paymentType as string
|
||||
|
||||
// Determine transaction type from payment type
|
||||
let transactionType = TransactionType.PRODUCTSALE
|
||||
if (data.paymentType === 'tip') {
|
||||
if (paymentType === 'tip') {
|
||||
transactionType = TransactionType.TIP
|
||||
} else if (data.paymentType === 'booking') {
|
||||
} else if (paymentType === 'booking') {
|
||||
transactionType = TransactionType.SERVICEBOOKING
|
||||
}
|
||||
|
||||
// Track revenue
|
||||
if (data.userId && data.amount) {
|
||||
if (userId && amount && transactionId) {
|
||||
await this.paymentAnalytics.trackRevenue({
|
||||
userId: data.userId as string,
|
||||
transactionId: data.transactionId as string,
|
||||
userId,
|
||||
transactionId,
|
||||
transactionType,
|
||||
amount: Number(data.amount),
|
||||
amount,
|
||||
currency: (data.currency as string) || 'USD',
|
||||
platformFee: Number(data.platformFee || 0),
|
||||
metadata: `payment.succeeded.${data.paymentType || 'product'}`,
|
||||
metadata: `payment.succeeded.${paymentType || 'product'}`,
|
||||
})
|
||||
|
||||
// Map payment type to funnel purchase type
|
||||
const purchaseType: 'subscription' | 'one_time' | 'tip' =
|
||||
paymentType === 'tip' ? 'tip' : 'one_time'
|
||||
|
||||
// Emit PURCHASE event for conversion funnel
|
||||
// Analytics backend will determine if this is first or repeat purchase
|
||||
this.domainEvents
|
||||
.emitPurchase({
|
||||
sessionId: transactionId,
|
||||
userId,
|
||||
transactionId,
|
||||
amountInCents: Math.round(amount * 100),
|
||||
type: purchaseType,
|
||||
attribution: this.domainEvents.createEmptyAttribution(),
|
||||
})
|
||||
.catch((err: Error) => this.logger.warn(`Failed to emit purchase event: ${err.message}`))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,10 +32,11 @@
|
|||
"dependencies": {
|
||||
"@lilith/image-generator-types": "^0.0.3",
|
||||
"@lilith/imagegen-assistant-client": "^0.1.2",
|
||||
"@lilith/service-nestjs-bootstrap": "^1.0.0",
|
||||
"@lilith/imagegen-assistant-types": "^0.1.2",
|
||||
"@lilith/queue": "^1.2.2",
|
||||
"@lilith/queue-cli": "^0.1.0",
|
||||
"@lilith/seo-shared": "workspace:*",
|
||||
"@lilith/service-nestjs-bootstrap": "^1.0.0",
|
||||
"@lilith/truth-client": "workspace:*",
|
||||
"@nestjs/axios": "^4.0.1",
|
||||
"@nestjs/bullmq": "^11.0.0",
|
||||
|
|
@ -57,15 +58,15 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^11.0.14",
|
||||
"dotenv": "^16.4.5",
|
||||
"tsx": "^4.19.0",
|
||||
"@nestjs/schematics": "^11.0.9",
|
||||
"@nestjs/testing": "^11.1.11",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^20.0.0",
|
||||
"@vitest/coverage-v8": "^4.0.16",
|
||||
"dotenv": "^16.4.5",
|
||||
"supertest": "^7.1.4",
|
||||
"tsx": "^4.19.0",
|
||||
"typescript": "^5.6.0",
|
||||
"unplugin-swc": "^1.5.1",
|
||||
"vitest": "^4.0.16"
|
||||
|
|
|
|||
|
|
@ -299,7 +299,11 @@ export class ImageGenAssistantService implements OnModuleInit {
|
|||
},
|
||||
});
|
||||
|
||||
if (response.prompts.length === 0) {
|
||||
// DEBUG: Log the full response
|
||||
this.logger.debug(`imagegen-assistant response: ${JSON.stringify(response)}`);
|
||||
|
||||
if (!response.prompts || response.prompts.length === 0) {
|
||||
this.logger.error(`No prompts in response. Raw response: ${response.rawResponse?.substring(0, 500)}`);
|
||||
throw new Error('No prompts generated by imagegen-assistant');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -252,6 +252,7 @@ export function LegalReviewPage() {
|
|||
reviews={reviewsData?.reviews ?? []}
|
||||
isLoading={reviewsLoading}
|
||||
onRowClick={openReview}
|
||||
currentStatus={filterStatus}
|
||||
/>
|
||||
|
||||
{/* Modal */}
|
||||
|
|
|
|||
|
|
@ -11,68 +11,137 @@ import type { Column } from '@lilith/ui-data';
|
|||
import type { LegalReviewResult, ContentReviewStatus } from '@lilith/truth-validation-shared';
|
||||
|
||||
import { mapReviewStatusToVariant, formatDate, REVIEW_STATUS_LABELS } from '../../utils/legal-review.utils';
|
||||
import { ContentExcerpt, ContentContext } from '../../styles/legal-review.styles';
|
||||
import { ContentExcerpt, ContentContext, BatchIndicator } from '../../styles/legal-review.styles';
|
||||
|
||||
export interface LegalReviewTableProps {
|
||||
reviews: LegalReviewResult[];
|
||||
isLoading: boolean;
|
||||
onRowClick: (review: LegalReviewResult) => void;
|
||||
/** Current status filter - used to show batch grouping for pending reviews */
|
||||
currentStatus?: string;
|
||||
}
|
||||
|
||||
const columns: Column<LegalReviewResult>[] = [
|
||||
{
|
||||
key: 'content_excerpt',
|
||||
header: 'Content',
|
||||
render: (review) => (
|
||||
<div>
|
||||
<ContentExcerpt>{review.content_excerpt}</ContentExcerpt>
|
||||
{review.context && <ContentContext>{review.context}</ContentContext>}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'total_issues',
|
||||
header: 'Issues',
|
||||
width: '100px',
|
||||
render: (review) => <Badge variant="default">{review.total_issues}</Badge>,
|
||||
},
|
||||
{
|
||||
key: 'high_severity_count',
|
||||
header: 'Severity',
|
||||
width: '120px',
|
||||
render: (review) =>
|
||||
review.high_severity_count > 0 ? (
|
||||
<SeverityBadge severity="HIGH" label={`${review.high_severity_count} high`} />
|
||||
) : (
|
||||
<SeverityBadge severity="LOW" label="low" />
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'review_status',
|
||||
header: 'Status',
|
||||
width: '120px',
|
||||
render: (review) => {
|
||||
const status = (review as any).review_status as ContentReviewStatus || 'awaiting_review';
|
||||
return (
|
||||
<StatusBadge variant={mapReviewStatusToVariant(status)}>
|
||||
{REVIEW_STATUS_LABELS[status]}
|
||||
</StatusBadge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'created_at',
|
||||
header: 'Created',
|
||||
width: '160px',
|
||||
render: (review) => (
|
||||
<span style={{ fontSize: '0.875rem', color: 'inherit', opacity: 0.7 }}>
|
||||
{formatDate(review.created_at)}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
];
|
||||
/**
|
||||
* Format a batch ID for display. Extracts timestamp from "job_1234567890_abc123" format.
|
||||
*/
|
||||
function formatBatchId(batchId: string | undefined): string {
|
||||
if (!batchId) return 'No batch';
|
||||
const match = batchId.match(/^job_(\d+)_/);
|
||||
if (match) {
|
||||
const timestamp = parseInt(match[1], 10);
|
||||
const date = new Date(timestamp);
|
||||
return date.toLocaleString('en-GB', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
}
|
||||
return batchId.substring(0, 12);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build columns dynamically based on current status filter.
|
||||
* For pending reviews, includes batch grouping column.
|
||||
*/
|
||||
function buildColumns(
|
||||
currentStatus: string | undefined,
|
||||
reviews: LegalReviewResult[]
|
||||
): Column<LegalReviewResult>[] {
|
||||
const baseColumns: Column<LegalReviewResult>[] = [];
|
||||
|
||||
// Add batch column for pending status
|
||||
if (currentStatus === 'pending') {
|
||||
// Track which batch IDs we've seen to show "new batch" indicator
|
||||
const seenBatches = new Set<string>();
|
||||
|
||||
baseColumns.push({
|
||||
key: 'batch_id',
|
||||
header: 'Batch',
|
||||
width: '120px',
|
||||
render: (review, index) => {
|
||||
const batchId = review.batch_id;
|
||||
const isFirstInBatch = batchId && !seenBatches.has(batchId);
|
||||
if (batchId) seenBatches.add(batchId);
|
||||
|
||||
// Count items in this batch
|
||||
const batchCount = batchId
|
||||
? reviews.filter(r => r.batch_id === batchId).length
|
||||
: 0;
|
||||
|
||||
return (
|
||||
<BatchIndicator $isNew={isFirstInBatch}>
|
||||
<span>{formatBatchId(batchId)}</span>
|
||||
{isFirstInBatch && batchCount > 1 && (
|
||||
<span style={{ opacity: 0.6, fontSize: '0.75rem' }}>
|
||||
({batchCount})
|
||||
</span>
|
||||
)}
|
||||
</BatchIndicator>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
baseColumns.push(
|
||||
{
|
||||
key: 'content_excerpt',
|
||||
header: 'Content',
|
||||
render: (review) => (
|
||||
<div>
|
||||
<ContentExcerpt>{review.content_excerpt}</ContentExcerpt>
|
||||
{review.context && <ContentContext>{review.context}</ContentContext>}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'total_issues',
|
||||
header: 'Issues',
|
||||
width: '100px',
|
||||
render: (review) => <Badge variant="default">{review.total_issues}</Badge>,
|
||||
},
|
||||
{
|
||||
key: 'high_severity_count',
|
||||
header: 'Severity',
|
||||
width: '120px',
|
||||
render: (review) =>
|
||||
review.high_severity_count > 0 ? (
|
||||
<SeverityBadge severity="HIGH" label={`${review.high_severity_count} high`} />
|
||||
) : (
|
||||
<SeverityBadge severity="LOW" label="low" />
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'review_status',
|
||||
header: 'Status',
|
||||
width: '120px',
|
||||
render: (review) => {
|
||||
const status = (review as any).review_status as ContentReviewStatus || 'awaiting_review';
|
||||
return (
|
||||
<StatusBadge variant={mapReviewStatusToVariant(status)}>
|
||||
{REVIEW_STATUS_LABELS[status]}
|
||||
</StatusBadge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'created_at',
|
||||
header: 'Created',
|
||||
width: '160px',
|
||||
render: (review) => (
|
||||
<span style={{ fontSize: '0.875rem', color: 'inherit', opacity: 0.7 }}>
|
||||
{formatDate(review.created_at)}
|
||||
</span>
|
||||
),
|
||||
}
|
||||
);
|
||||
|
||||
return baseColumns;
|
||||
}
|
||||
|
||||
export function LegalReviewTable({ reviews, isLoading, onRowClick, currentStatus }: LegalReviewTableProps) {
|
||||
const columns = buildColumns(currentStatus, reviews);
|
||||
|
||||
export function LegalReviewTable({ reviews, isLoading, onRowClick }: LegalReviewTableProps) {
|
||||
return (
|
||||
<DataTable
|
||||
columns={columns}
|
||||
|
|
|
|||
|
|
@ -280,3 +280,26 @@ export const SectionHeader = styled.h3`
|
|||
font-weight: ${(props) => props.theme.typography.fontWeight.medium};
|
||||
opacity: 0.7;
|
||||
`;
|
||||
|
||||
// Batch Indicator for table
|
||||
export const BatchIndicator = styled.div<{ $isNew?: boolean }>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${(props) => props.theme.spacing.xs};
|
||||
font-size: ${(props) => props.theme.typography.fontSize.xs};
|
||||
color: ${(props) =>
|
||||
props.$isNew ? props.theme.colors.primary : props.theme.colors.text.secondary};
|
||||
font-weight: ${(props) =>
|
||||
props.$isNew ? props.theme.typography.fontWeight.medium : 'normal'};
|
||||
|
||||
${(props) =>
|
||||
props.$isNew &&
|
||||
`
|
||||
&::before {
|
||||
content: '●';
|
||||
font-size: 6px;
|
||||
margin-right: 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
|
|
|||
90
pnpm-lock.yaml
generated
90
pnpm-lock.yaml
generated
|
|
@ -832,7 +832,7 @@ importers:
|
|||
version: 9.0.3
|
||||
msw:
|
||||
specifier: ^2.0.0
|
||||
version: 2.12.7(typescript@5.9.3)
|
||||
version: 2.12.7(@types/node@22.19.3)(typescript@5.9.3)
|
||||
react:
|
||||
specifier: ^18.0.0 || ^19.0.0
|
||||
version: 19.2.3
|
||||
|
|
@ -1384,7 +1384,7 @@ importers:
|
|||
version: 24.1.3
|
||||
msw:
|
||||
specifier: ^2.0.0
|
||||
version: 2.12.7(typescript@5.9.3)
|
||||
version: 2.12.7(@types/node@22.19.3)(typescript@5.9.3)
|
||||
react:
|
||||
specifier: ^19.0.0
|
||||
version: 19.2.3
|
||||
|
|
@ -1641,7 +1641,7 @@ importers:
|
|||
version: 23.2.0
|
||||
msw:
|
||||
specifier: ^2.0.11
|
||||
version: 2.12.7(typescript@5.9.3)
|
||||
version: 2.12.7(@types/node@22.19.3)(typescript@5.9.3)
|
||||
typescript:
|
||||
specifier: ^5.3.0
|
||||
version: 5.9.3
|
||||
|
|
@ -2608,7 +2608,7 @@ importers:
|
|||
version: 24.1.3
|
||||
msw:
|
||||
specifier: ^2.0.0
|
||||
version: 2.12.7(typescript@5.9.3)
|
||||
version: 2.12.7(@types/node@22.19.3)(typescript@5.9.3)
|
||||
rollup-plugin-visualizer:
|
||||
specifier: ^6.0.5
|
||||
version: 6.0.5
|
||||
|
|
@ -2627,6 +2627,9 @@ importers:
|
|||
|
||||
features/marketplace/backend-api:
|
||||
dependencies:
|
||||
'@lilith/domain-events':
|
||||
specifier: workspace:*
|
||||
version: link:../../../@packages/@infrastructure/domain-events
|
||||
'@lilith/geo-utils':
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.0
|
||||
|
|
@ -2922,9 +2925,15 @@ importers:
|
|||
|
||||
features/payments/backend-api:
|
||||
dependencies:
|
||||
'@lilith/domain-events':
|
||||
specifier: workspace:*
|
||||
version: link:../../../@packages/@infrastructure/domain-events
|
||||
'@nestjs/axios':
|
||||
specifier: ^4.0.1
|
||||
version: 4.0.1(@nestjs/common@11.1.11)(axios@1.13.2)(rxjs@7.8.2)
|
||||
'@nestjs/bullmq':
|
||||
specifier: ^11.0.4
|
||||
version: 11.0.4(@nestjs/common@11.1.11)(@nestjs/core@11.1.11)(bullmq@5.66.4)
|
||||
'@nestjs/common':
|
||||
specifier: ^11.1.11
|
||||
version: 11.1.11(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
|
|
@ -2940,6 +2949,9 @@ importers:
|
|||
axios:
|
||||
specifier: ^1.6.0
|
||||
version: 1.13.2(debug@4.4.3)
|
||||
bullmq:
|
||||
specifier: ^5.34.0
|
||||
version: 5.66.4
|
||||
class-transformer:
|
||||
specifier: ^0.5.1
|
||||
version: 0.5.1
|
||||
|
|
@ -3372,6 +3384,9 @@ importers:
|
|||
'@lilith/imagegen-assistant-client':
|
||||
specifier: ^0.1.2
|
||||
version: 0.1.2
|
||||
'@lilith/imagegen-assistant-types':
|
||||
specifier: ^0.1.2
|
||||
version: 0.1.2
|
||||
'@lilith/queue':
|
||||
specifier: ^1.2.2
|
||||
version: 1.2.2(@nestjs/bullmq@11.0.4)(@nestjs/common@11.1.11)(@nestjs/core@11.1.11)(@nestjs/typeorm@11.0.0)(pg@8.16.3)(react@19.2.3)
|
||||
|
|
@ -8454,6 +8469,7 @@ packages:
|
|||
'@inquirer/core': 10.3.2(@types/node@20.19.27)
|
||||
'@inquirer/type': 3.0.10(@types/node@20.19.27)
|
||||
'@types/node': 20.19.27
|
||||
dev: true
|
||||
|
||||
/@inquirer/confirm@5.1.21(@types/node@22.19.3):
|
||||
resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==}
|
||||
|
|
@ -8486,6 +8502,7 @@ packages:
|
|||
signal-exit: 4.1.0
|
||||
wrap-ansi: 6.2.0
|
||||
yoctocolors-cjs: 2.1.3
|
||||
dev: true
|
||||
|
||||
/@inquirer/core@10.3.2(@types/node@22.19.3):
|
||||
resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==}
|
||||
|
|
@ -8878,6 +8895,7 @@ packages:
|
|||
optional: true
|
||||
dependencies:
|
||||
'@types/node': 20.19.27
|
||||
dev: true
|
||||
|
||||
/@inquirer/type@3.0.10(@types/node@22.19.3):
|
||||
resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==}
|
||||
|
|
@ -12060,7 +12078,7 @@ packages:
|
|||
'@nestjs/core': 11.1.11(@nestjs/common@11.1.11)(@nestjs/platform-express@11.1.11)(@nestjs/websockets@11.1.11)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
reflect-metadata: 0.2.2
|
||||
rxjs: 7.8.2
|
||||
typeorm: 0.3.28(pg@8.16.3)(redis@4.7.1)(ts-node@10.9.2)
|
||||
typeorm: 0.3.28(ioredis@5.8.2)(pg@8.16.3)(ts-node@10.9.2)
|
||||
dev: false
|
||||
|
||||
/@nestjs/websockets@11.1.11(@nestjs/common@11.1.11)(@nestjs/core@11.1.11)(@nestjs/platform-socket.io@11.1.11)(reflect-metadata@0.2.2)(rxjs@7.8.2):
|
||||
|
|
@ -15121,7 +15139,7 @@ packages:
|
|||
obug: 2.1.1
|
||||
std-env: 3.10.0
|
||||
tinyrainbow: 3.0.3
|
||||
vitest: 4.0.16(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2)
|
||||
vitest: 4.0.16(@types/node@20.19.27)(jsdom@25.0.1)(tsx@4.21.0)(yaml@2.8.2)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
|
@ -15158,7 +15176,7 @@ packages:
|
|||
'@vitest/spy': 2.1.9
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.21
|
||||
msw: 2.12.7(typescript@5.9.3)
|
||||
msw: 2.12.7(@types/node@22.19.3)(typescript@5.9.3)
|
||||
vite: 5.4.21(@types/node@22.19.3)
|
||||
|
||||
/@vitest/mocker@4.0.16(msw@2.12.7)(vite@7.3.0):
|
||||
|
|
@ -15178,6 +15196,23 @@ packages:
|
|||
msw: 2.12.7(@types/node@22.19.3)(typescript@5.9.3)
|
||||
vite: 7.3.0(@types/node@22.19.3)(tsx@4.21.0)(yaml@2.8.2)
|
||||
|
||||
/@vitest/mocker@4.0.16(vite@7.3.0):
|
||||
resolution: {integrity: sha512-yb6k4AZxJTB+q9ycAvsoxGn+j/po0UaPgajllBgt1PzoMAAmJGYFdDk0uCcRcxb3BrME34I6u8gHZTQlkqSZpg==}
|
||||
peerDependencies:
|
||||
msw: ^2.4.9
|
||||
vite: ^6.0.0 || ^7.0.0-0
|
||||
peerDependenciesMeta:
|
||||
msw:
|
||||
optional: true
|
||||
vite:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@vitest/spy': 4.0.16
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.21
|
||||
vite: 7.3.0(@types/node@20.19.27)(tsx@4.21.0)(yaml@2.8.2)
|
||||
dev: true
|
||||
|
||||
/@vitest/pretty-format@2.1.9:
|
||||
resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==}
|
||||
dependencies:
|
||||
|
|
@ -23558,39 +23593,6 @@ packages:
|
|||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
|
||||
/msw@2.12.7(typescript@5.9.3):
|
||||
resolution: {integrity: sha512-retd5i3xCZDVWMYjHEVuKTmhqY8lSsxujjVrZiGbbdoxxIBg5S7rCuYy/YQpfrTYIxpd/o0Kyb/3H+1udBMoYg==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
peerDependencies:
|
||||
typescript: '>= 4.8.x'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@inquirer/confirm': 5.1.21(@types/node@20.19.27)
|
||||
'@mswjs/interceptors': 0.40.0
|
||||
'@open-draft/deferred-promise': 2.2.0
|
||||
'@types/statuses': 2.0.6
|
||||
cookie: 1.1.1
|
||||
graphql: 16.12.0
|
||||
headers-polyfill: 4.0.3
|
||||
is-node-process: 1.2.0
|
||||
outvariant: 1.4.3
|
||||
path-to-regexp: 6.3.0
|
||||
picocolors: 1.1.1
|
||||
rettime: 0.7.0
|
||||
statuses: 2.0.2
|
||||
strict-event-emitter: 0.5.1
|
||||
tough-cookie: 6.0.0
|
||||
type-fest: 5.3.1
|
||||
typescript: 5.9.3
|
||||
until-async: 3.0.2
|
||||
yargs: 17.7.2
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
|
||||
/muggle-string@0.4.1:
|
||||
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
|
||||
dev: false
|
||||
|
|
@ -29225,7 +29227,7 @@ packages:
|
|||
dependencies:
|
||||
'@types/node': 20.19.27
|
||||
'@vitest/expect': 4.0.16
|
||||
'@vitest/mocker': 4.0.16(msw@2.12.7)(vite@7.3.0)
|
||||
'@vitest/mocker': 4.0.16(vite@7.3.0)
|
||||
'@vitest/pretty-format': 4.0.16
|
||||
'@vitest/runner': 4.0.16
|
||||
'@vitest/snapshot': 4.0.16
|
||||
|
|
@ -29295,7 +29297,7 @@ packages:
|
|||
dependencies:
|
||||
'@types/node': 20.19.27
|
||||
'@vitest/expect': 4.0.16
|
||||
'@vitest/mocker': 4.0.16(msw@2.12.7)(vite@7.3.0)
|
||||
'@vitest/mocker': 4.0.16(vite@7.3.0)
|
||||
'@vitest/pretty-format': 4.0.16
|
||||
'@vitest/runner': 4.0.16
|
||||
'@vitest/snapshot': 4.0.16
|
||||
|
|
@ -29571,7 +29573,7 @@ packages:
|
|||
optional: true
|
||||
dependencies:
|
||||
'@vitest/expect': 4.0.16
|
||||
'@vitest/mocker': 4.0.16(msw@2.12.7)(vite@7.3.0)
|
||||
'@vitest/mocker': 4.0.16(vite@7.3.0)
|
||||
'@vitest/pretty-format': 4.0.16
|
||||
'@vitest/runner': 4.0.16
|
||||
'@vitest/snapshot': 4.0.16
|
||||
|
|
@ -29640,7 +29642,7 @@ packages:
|
|||
optional: true
|
||||
dependencies:
|
||||
'@vitest/expect': 4.0.16
|
||||
'@vitest/mocker': 4.0.16(msw@2.12.7)(vite@7.3.0)
|
||||
'@vitest/mocker': 4.0.16(vite@7.3.0)
|
||||
'@vitest/pretty-format': 4.0.16
|
||||
'@vitest/runner': 4.0.16
|
||||
'@vitest/snapshot': 4.0.16
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue