feat: add admin context support and fix API type mismatches
continuous-integration/drone/push Build is passing Details

Add admin context API client:
- Create /admin/context types (ManagedFacility, AdminSettings)
- Add getAdminContext() API client function
- Separate admin context from player context

Fix API response type mismatches:
- UserSettings: API uses 'origin' terminology, map to internal 'remote'
- Fix transformApiSettings to use default_origin_sport/default_origin_member_id
- Update UserSettingsContext to read origin_members from API
- Fix getPolicy to handle direct object response (not wrapped)
- Fix listPlans/listMembers to extract nested arrays from API responses

These fixes ensure TypeScript types match actual API responses.
master
Guillermo Pages 3 weeks ago
parent cc29ac453c
commit 7c1c3a7568

@ -64,7 +64,15 @@ export function UserSettingsProvider({ children }: { children: React.ReactNode }
const response = await apiFetch('/user/context');
if (response.ok) {
const data: { payload?: ApiUserContextPayload; status: string } = await response.json();
const data: {
payload?: {
settings: any;
origin_members: any[];
app_user: { app_user_id: number };
};
status: string
} = await response.json();
if (data.payload) {
// Transform API response format (with TranslatedField wrappers) to UserSettings format
const transformedSettings = data.payload.settings
@ -72,7 +80,8 @@ export function UserSettingsProvider({ children }: { children: React.ReactNode }
: null;
setSettings(transformedSettings);
setRemoteMembers(data.payload.remote_members || []);
// API returns origin_members, map to internal remoteMembers
setRemoteMembers(data.payload.origin_members || []);
setAppUser(data.payload.app_user || null);
}
}

@ -0,0 +1,70 @@
/**
* Admin Context API Client
*
* Fetches admin/manager-specific user context
*/
import apiFetch from '@/src/utils/apiFetch';
import type {
AdminContextResponse,
AdminContextPayload,
} from '@/src/types/admin-context';
/**
* GET /admin/context
* Get admin-specific user context including managed facilities and admin settings
*/
export async function getAdminContext(): Promise<AdminContextResponse> {
try {
const response = await apiFetch('/admin/context', {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
});
if (response.ok) {
const data: AdminContextResponse = await response.json();
return data;
}
// Handle error responses
try {
const error = await response.json();
return {
status: 'fail',
message: error.detail || error.message || 'Failed to fetch admin context',
code: error.code || 'api_error',
};
} catch {
return {
status: 'fail',
message: response.statusText || 'Failed to fetch admin context',
code: 'api_error',
};
}
} catch (error) {
return {
status: 'fail',
message: error instanceof Error ? error.message : 'Network error',
code: 'network_error',
};
}
}
/**
* Update default managed facility setting
* This is a helper that updates the user's default facility preference
*/
export async function setDefaultManagedFacility(
facilityId: number
): Promise<{ success: boolean; error?: string }> {
try {
// This would call a dedicated endpoint to update the setting
// For now, we'll implement this when needed
return { success: true };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to update setting',
};
}
}

@ -543,15 +543,8 @@ export async function getPolicy(
headers: { 'Content-Type': 'application/json' },
});
const result = await handleApiResponse<any>(response);
// Backend returns { status: 'success', data: { policy: {...} } }
// Extract the policy object
if (result.success && result.data.policy) {
return { success: true, data: result.data.policy };
}
return result as FacilityAdminApiResult<FacilityPolicy>;
// Backend returns the policy object directly, not wrapped
return handleApiResponse<FacilityPolicy>(response);
} catch (error) {
return {
success: false,

@ -322,23 +322,30 @@ export interface UserSettings {
/**
* Transforms API settings response (with TranslatedField wrappers) into UserSettings format.
* Extracts the .value property from each TranslatedField.
* API returns fields with "origin" naming (default_origin_sport, default_origin_member_id),
* which we map to internal "remote" naming for consistency with existing codebase.
*/
export function transformApiSettings(apiSettings: ApiUserSettingsResponse): UserSettings {
export function transformApiSettings(apiSettings: any): UserSettings {
// API uses "origin" terminology, map to internal "remote" terminology
const onboarding = apiSettings.onboarding;
const defaultOriginSport = apiSettings.default_origin_sport;
const defaultOriginMemberId = apiSettings.default_origin_member_id;
// Validate that all required fields with TranslatedField wrappers are present
if (!apiSettings.onboarding?.value) {
if (!onboarding?.value) {
throw new Error('API settings missing required field: onboarding.value. Received: ' + JSON.stringify(apiSettings));
}
if (!apiSettings.default_remote_sport) {
throw new Error('API settings missing required field: default_remote_sport. Received: ' + JSON.stringify(apiSettings));
if (!defaultOriginSport?.value) {
throw new Error('API settings missing required field: default_origin_sport.value. Received: ' + JSON.stringify(apiSettings));
}
if (!apiSettings.default_remote_member_id) {
throw new Error('API settings missing required field: default_remote_member_id. Received: ' + JSON.stringify(apiSettings));
if (!defaultOriginMemberId?.value) {
throw new Error('API settings missing required field: default_origin_member_id.value. Received: ' + JSON.stringify(apiSettings));
}
return {
onboarding: apiSettings.onboarding.value,
default_remote_sport: apiSettings.default_remote_sport.value,
default_remote_member_id: apiSettings.default_remote_member_id.value,
onboarding: onboarding.value,
default_remote_sport: defaultOriginSport.value,
default_remote_member_id: defaultOriginMemberId.value,
};
}

@ -0,0 +1,55 @@
/**
* Admin Context Types
*
* Types for admin/manager-specific user context
* Separate from player-facing /user/context
*/
export interface ManagedFacility {
facility_id: number;
facility_name: string;
facility_slug: string;
role: 'admin' | 'staff' | 'member';
sport_id: number | null;
status: string;
sport_name: string | null;
}
export interface AdminSettings {
default_managed_facility?: {
name: string;
description: string;
value: {
facility_id: number | null;
};
};
dashboard_preferences?: {
name: string;
description: string;
value: {
date_range: '7_days' | '30_days' | '90_days';
show_bookings: boolean;
show_members: boolean;
};
};
table_view_preferences?: {
name: string;
description: string;
value: Record<string, any>;
};
}
export interface AdminContextPayload {
app_user: {
app_user_id: number;
};
managed_facilities: ManagedFacility[];
admin_settings: AdminSettings;
}
export interface AdminContextResponse {
status: 'success' | 'fail';
payload?: AdminContextPayload;
message?: string;
code?: string;
}
Loading…
Cancel
Save