diff --git a/frontends/nextjs/src/lib/hooks/use-rest-api.ts b/frontends/nextjs/src/lib/hooks/use-rest-api.ts index 089b1ad5b..9dcc18aac 100644 --- a/frontends/nextjs/src/lib/hooks/use-rest-api.ts +++ b/frontends/nextjs/src/lib/hooks/use-rest-api.ts @@ -69,22 +69,30 @@ export function useRestApi(options?: UseRestApiOptions) { // Try to get tenant from context, fall back to options const tenantContext = useTenantOptional() const tenant = options?.tenant || tenantContext?.tenant - const packageId = options?.packageId || tenantContext?.packageId + const defaultPackageId = options?.packageId || tenantContext?.packageId /** * Build the base URL for API calls + * @param entity Entity name + * @param id Optional record ID + * @param action Optional action name + * @param pkgOverride Override the package (for dependency package access) */ const buildUrl = useCallback( - (entity: string, id?: string, action?: string) => { - if (!tenant || !packageId) { - throw new Error('Tenant and package are required') + (entity: string, id?: string, action?: string, pkgOverride?: string) => { + if (!tenant) { + throw new Error('Tenant is required') } - let url = `/api/v1/${tenant}/${packageId}/${entity}` + const pkg = pkgOverride || defaultPackageId + if (!pkg) { + throw new Error('Package is required') + } + let url = `/api/v1/${tenant}/${pkg}/${entity}` if (id) url += `/${id}` if (action) url += `/${action}` return url }, - [tenant, packageId] + [tenant, defaultPackageId] ) /** @@ -96,7 +104,8 @@ export function useRestApi(options?: UseRestApiOptions) { setError(null) try { - const url = buildUrl(entity) + buildQueryString(options || {}) + const { packageId: pkgOverride, ...queryOpts } = options || {} + const url = buildUrl(entity, undefined, undefined, pkgOverride) + buildQueryString(queryOpts) const response = await fetch(url) const json: ApiResponse = await response.json() @@ -307,3 +316,40 @@ export function useEntity(entity: string, options?: UseRestApiOptio api.action(entity, id, actionName, data), } } + +/** + * Hook for accessing entities from a dependency package + * + * @example + * // On a forum_forge page, access user_manager entities + * const { list: listRoles } = useDependencyEntity('user_manager', 'roles') + * const roles = await listRoles() + */ +export function useDependencyEntity( + packageId: string, + entity: string +) { + const tenantContext = useTenantOptional() + + // Verify package is accessible (either primary or a dependency) + if (tenantContext && !tenantContext.hasPackage(packageId)) { + console.warn( + `Package '${packageId}' is not accessible from '${tenantContext.primaryPackage}'. ` + + `Add it to dependencies in metadata.json.` + ) + } + + const api = useRestApi({ packageId }) + + return { + loading: api.loading, + error: api.error, + list: (opts?: Omit) => api.list(entity, opts), + read: (id: string) => api.read(entity, id), + create: (data: Record) => api.create(entity, data), + update: (id: string, data: Record) => api.update(entity, id, data), + remove: (id: string) => api.remove(entity, id), + action: (id: string, actionName: string, data?: Record) => + api.action(entity, id, actionName, data), + } +}