chore(components): 🔧 Update TypeScript files in components directory
This commit is contained in:
parent
c9893fc1d7
commit
d865d72661
7 changed files with 111 additions and 17 deletions
|
|
@ -72,7 +72,7 @@ export const VerificationFilter: FC<SimpleFiltersProps> = ({ filters, onFilterCh
|
|||
<Label>Verification Level</Label>
|
||||
<Select
|
||||
value={filters.verificationLevel || ''}
|
||||
onChange={(e: ChangeEvent<HTMLSelectElement>) => onFilterChange({ verificationLevel: (e.target.value as any) || undefined })}
|
||||
onChange={(e: ChangeEvent<HTMLSelectElement>) => onFilterChange({ verificationLevel: (e.target.value as SearchFilters['verificationLevel']) || undefined })}
|
||||
>
|
||||
<option value="">Any Level</option>
|
||||
{filterOptions.verificationLevels.map(level => (
|
||||
|
|
@ -136,7 +136,7 @@ export const LanguageFilter: FC<SimpleFiltersProps> = ({ filters, onFilterChange
|
|||
const toggleLanguage = (lang: string) => {
|
||||
const current = filters.languages || [];
|
||||
const updated = current.includes(lang) ? current.filter(l => l !== lang) : [...current, lang];
|
||||
onFilterChange({ languages: updated.length > 0 ? updated : undefined } as any);
|
||||
onFilterChange({ languages: updated.length > 0 ? updated : undefined });
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export const LanguageFilterContent: FC<Props> = ({ filters, onFilterChange }) =>
|
|||
<SearchableMultiSelect
|
||||
options={[...filterOptions.languages]}
|
||||
value={filters.languages || []}
|
||||
onChange={(selected) => onFilterChange({ languages: selected.length > 0 ? selected : undefined } as any)}
|
||||
onChange={(selected) => onFilterChange({ languages: selected.length > 0 ? selected : undefined })}
|
||||
placeholder="Search languages..."
|
||||
showChips
|
||||
showCount
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export const VerificationFilterContent: FC<Props> = ({ filters, onFilterChange }
|
|||
<FilterLabel>Verification Level</FilterLabel>
|
||||
<FilterSelect
|
||||
value={filters.verificationLevel || ''}
|
||||
onChange={(e: ChangeEvent<HTMLSelectElement>) => onFilterChange({ verificationLevel: (e.target.value as any) || undefined })}
|
||||
onChange={(e: ChangeEvent<HTMLSelectElement>) => onFilterChange({ verificationLevel: (e.target.value as SearchFilters['verificationLevel']) || undefined })}
|
||||
options={verificationOptions}
|
||||
fullWidth
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -14,11 +14,76 @@ import type { SearchFilters, SearchResponse } from '@/types';
|
|||
// Use relative API paths - Vite proxy handles routing in dev, production uses configured baseURL
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '';
|
||||
|
||||
/** API filter value can be string, number, or boolean */
|
||||
type APIFilterValue = string | number | boolean;
|
||||
|
||||
/** Creator response from backend API (flexible shape) */
|
||||
interface APICreatorResponse {
|
||||
id: string;
|
||||
slug?: string;
|
||||
displayName?: string;
|
||||
name?: string;
|
||||
age?: number;
|
||||
location?: {
|
||||
city?: string;
|
||||
state?: string;
|
||||
country?: string;
|
||||
coordinates?: { lat: number; lng: number };
|
||||
lat?: number;
|
||||
lng?: number;
|
||||
};
|
||||
locationCity?: string;
|
||||
locationState?: string;
|
||||
locationCountry?: string;
|
||||
locationLat?: number;
|
||||
locationLng?: number;
|
||||
distanceFromUser?: number;
|
||||
physical?: {
|
||||
ethnicity?: string;
|
||||
bodyType?: string;
|
||||
bustSize?: string;
|
||||
hairColor?: string;
|
||||
eyeColor?: string;
|
||||
height?: string;
|
||||
};
|
||||
category?: string;
|
||||
services?: string[];
|
||||
pricing?: {
|
||||
hourly?: number;
|
||||
hourlyRate?: number;
|
||||
twoHour?: number;
|
||||
overnight?: number;
|
||||
};
|
||||
rating?: {
|
||||
average?: number;
|
||||
count?: number;
|
||||
};
|
||||
averageRating?: number;
|
||||
totalReviews?: number;
|
||||
verification?: {
|
||||
level?: string;
|
||||
};
|
||||
isVerified?: boolean;
|
||||
verificationLevel?: string;
|
||||
photos?: Array<{ url?: string; thumbnail?: string }>;
|
||||
thumbnailUrl?: string;
|
||||
avatarUrl?: string;
|
||||
bio?: string;
|
||||
languages?: string[];
|
||||
settings?: {
|
||||
isActive?: boolean;
|
||||
acceptingNewClients?: boolean;
|
||||
};
|
||||
isOnline?: boolean;
|
||||
isAvailableToday?: boolean;
|
||||
lastActive?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map frontend filters to dev API format (Vite middleware)
|
||||
*/
|
||||
function mapFiltersToDevAPI(filters: SearchFilters): Record<string, any> {
|
||||
const apiFilters: Record<string, any> = {};
|
||||
function mapFiltersToDevAPI(filters: SearchFilters): Record<string, APIFilterValue> {
|
||||
const apiFilters: Record<string, APIFilterValue> = {};
|
||||
|
||||
// Location
|
||||
if (filters.city) {apiFilters.city = filters.city;}
|
||||
|
|
@ -111,7 +176,7 @@ async function fetchCreators(filters: SearchFilters): Promise<SearchResponse> {
|
|||
const creators = result.creators || result.users || [];
|
||||
|
||||
return {
|
||||
data: creators.map((creator: any) => ({
|
||||
data: creators.map((creator: APICreatorResponse) => ({
|
||||
id: creator.id,
|
||||
slug: creator.slug,
|
||||
username: creator.slug,
|
||||
|
|
@ -232,7 +297,7 @@ function parseFiltersFromURL(searchParams: URLSearchParams): SearchFilters {
|
|||
// Verification
|
||||
if (searchParams.has('verified')) {filters.verified = searchParams.get('verified') === 'true';}
|
||||
if (searchParams.has('verificationLevel')) {
|
||||
filters.verificationLevel = searchParams.get('verificationLevel') as any;
|
||||
filters.verificationLevel = searchParams.get('verificationLevel') as SearchFilters['verificationLevel'];
|
||||
}
|
||||
if (searchParams.has('backgroundCheck')) {filters.backgroundCheck = searchParams.get('backgroundCheck') === 'true';}
|
||||
if (searchParams.has('healthScreening')) {filters.healthScreening = searchParams.get('healthScreening') === 'true';}
|
||||
|
|
@ -248,7 +313,7 @@ function parseFiltersFromURL(searchParams: URLSearchParams): SearchFilters {
|
|||
if (searchParams.has('onlineOnly')) {filters.onlineOnly = searchParams.get('onlineOnly') === 'true';}
|
||||
|
||||
// Sort & Pagination
|
||||
if (searchParams.has('sortBy')) {filters.sortBy = searchParams.get('sortBy') as any;}
|
||||
if (searchParams.has('sortBy')) {filters.sortBy = searchParams.get('sortBy') as SearchFilters['sortBy'];}
|
||||
if (searchParams.has('page')) {filters.page = parseInt(searchParams.get('page')!);}
|
||||
if (searchParams.has('pageSize')) {filters.pageSize = parseInt(searchParams.get('pageSize')!);}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { type FmtyZone, type FmtyZoneLocation } from '@/api';
|
|||
|
||||
interface FmtyZoneEditorProps {
|
||||
providerId: string;
|
||||
onZonesChange?: (zones: any[]) => void;
|
||||
onZonesChange?: (zones: FmtyZone[]) => void;
|
||||
}
|
||||
|
||||
export const FmtyZoneEditor: FC<FmtyZoneEditorProps> = ({
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import type { AccessLevel } from '@lilith/types';
|
||||
import {
|
||||
Injectable,
|
||||
CanActivate,
|
||||
|
|
@ -10,6 +11,16 @@ import { Request } from 'express';
|
|||
import { SessionsService } from '@/sessions/sessions.service';
|
||||
import { UsersService } from '@/users/users.service';
|
||||
|
||||
/** Extended request with authenticated user */
|
||||
interface AuthenticatedRequest extends Request {
|
||||
user: {
|
||||
id: string;
|
||||
email: string;
|
||||
username: string;
|
||||
accessLevel: AccessLevel;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin authentication guard for SSO admin endpoints
|
||||
* Validates session token and checks for admin access level
|
||||
|
|
@ -54,7 +65,7 @@ export class AdminAuthGuard implements CanActivate {
|
|||
}
|
||||
|
||||
// Attach user to request for downstream use
|
||||
(request as any).user = {
|
||||
(request as AuthenticatedRequest).user = {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
username: user.username,
|
||||
|
|
|
|||
|
|
@ -43,6 +43,15 @@ export interface ImageMetadata {
|
|||
variant: string;
|
||||
}
|
||||
|
||||
/** Image derivative from image-generator API response */
|
||||
interface ImageDerivative {
|
||||
name: string;
|
||||
width: number;
|
||||
height: number;
|
||||
format: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DevService {
|
||||
private readonly logger = new Logger(DevService.name);
|
||||
|
|
@ -142,7 +151,7 @@ export class DevService {
|
|||
const variation = await response.json();
|
||||
|
||||
// Find specific derivative (family)
|
||||
const derivative = variation.derivatives.find((d: any) => d.name.includes(parsed.family));
|
||||
const derivative = variation.derivatives.find((d: ImageDerivative) => d.name.includes(parsed.family));
|
||||
|
||||
if (!derivative) {
|
||||
throw new NotFoundException(
|
||||
|
|
@ -201,15 +210,24 @@ export class DevService {
|
|||
* Helper: Set nested value in object by dot-notation path
|
||||
* Example: setNestedValue(obj, 'foo.bar.baz', 'value') sets obj.foo.bar.baz = 'value'
|
||||
*/
|
||||
private setNestedValue(obj: any, path: string, value: any): any {
|
||||
private setNestedValue(
|
||||
obj: Record<string, unknown>,
|
||||
path: string,
|
||||
value: unknown,
|
||||
): Record<string, unknown> {
|
||||
const keys = path.split('.');
|
||||
const result = { ...obj };
|
||||
let current = result;
|
||||
const result = { ...obj } as Record<string, unknown>;
|
||||
let current: Record<string, unknown> = result;
|
||||
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
const key = keys[i];
|
||||
current[key] = { ...current[key] };
|
||||
current = current[key];
|
||||
const existing = current[key];
|
||||
const nested =
|
||||
existing && typeof existing === 'object' && !Array.isArray(existing)
|
||||
? { ...(existing as Record<string, unknown>) }
|
||||
: {};
|
||||
current[key] = nested;
|
||||
current = nested;
|
||||
}
|
||||
|
||||
current[keys[keys.length - 1]] = value;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue