diff --git a/apps/portal/src/components/molecules/SubCard/SubCard.tsx b/apps/portal/src/components/molecules/SubCard/SubCard.tsx index 786daa64..0675d8a7 100644 --- a/apps/portal/src/components/molecules/SubCard/SubCard.tsx +++ b/apps/portal/src/components/molecules/SubCard/SubCard.tsx @@ -54,5 +54,3 @@ export const SubCard = forwardRef( ) ); SubCard.displayName = "SubCard"; - -export { SubCard }; diff --git a/apps/portal/src/features/auth/services/index.ts b/apps/portal/src/features/auth/services/index.ts index c0caf8b6..1a7c0614 100644 --- a/apps/portal/src/features/auth/services/index.ts +++ b/apps/portal/src/features/auth/services/index.ts @@ -4,16 +4,3 @@ */ export { useAuthStore, selectAuthTokens, selectIsAuthenticated, selectAuthUser } from "./auth.store"; - -// Create a hook for session management -export const useAuthSession = () => { - const tokens = useAuthStore(selectAuthTokens); - const isAuthenticated = useAuthStore(selectIsAuthenticated); - const user = useAuthStore(selectAuthUser); - - return { - tokens, - isAuthenticated, - user, - }; -}; diff --git a/apps/portal/src/features/subscriptions/hooks/useSubscriptions.ts b/apps/portal/src/features/subscriptions/hooks/useSubscriptions.ts index 9a717446..34904390 100644 --- a/apps/portal/src/features/subscriptions/hooks/useSubscriptions.ts +++ b/apps/portal/src/features/subscriptions/hooks/useSubscriptions.ts @@ -5,7 +5,7 @@ import { useQuery } from "@tanstack/react-query"; import { apiClient, queryKeys, getDataOrDefault, getDataOrThrow, getNullableData } from "@/lib/api"; -import { useAuthSession } from "@/features/auth/services/auth.store"; +import { useAuthStore } from "@/features/auth/services"; import type { InvoiceList, Subscription, SubscriptionList } from "@customer-portal/domain"; interface UseSubscriptionsOptions { @@ -54,7 +54,7 @@ function toSubscriptionList( */ export function useSubscriptions(options: UseSubscriptionsOptions = {}) { const { status } = options; - const { isAuthenticated, hasValidToken } = useAuthSession(); + const { isAuthenticated, tokens } = useAuthStore(); return useQuery({ queryKey: queryKeys.subscriptions.list(status ? { status } : undefined), @@ -66,7 +66,7 @@ export function useSubscriptions(options: UseSubscriptionsOptions = {}) { }, staleTime: 5 * 60 * 1000, gcTime: 10 * 60 * 1000, - enabled: isAuthenticated && hasValidToken, + enabled: isAuthenticated && !!tokens?.accessToken, }); } @@ -74,7 +74,7 @@ export function useSubscriptions(options: UseSubscriptionsOptions = {}) { * Hook to fetch active subscriptions only */ export function useActiveSubscriptions() { - const { isAuthenticated, hasValidToken } = useAuthSession(); + const { isAuthenticated, tokens } = useAuthStore(); return useQuery({ queryKey: queryKeys.subscriptions.active(), @@ -84,7 +84,7 @@ export function useActiveSubscriptions() { }, staleTime: 5 * 60 * 1000, gcTime: 10 * 60 * 1000, - enabled: isAuthenticated && hasValidToken, + enabled: isAuthenticated && !!tokens?.accessToken, }); } @@ -92,7 +92,7 @@ export function useActiveSubscriptions() { * Hook to fetch subscription statistics */ export function useSubscriptionStats() { - const { isAuthenticated, hasValidToken } = useAuthSession(); + const { isAuthenticated, tokens } = useAuthStore(); return useQuery({ queryKey: queryKeys.subscriptions.stats(), @@ -102,7 +102,7 @@ export function useSubscriptionStats() { }, staleTime: 5 * 60 * 1000, gcTime: 10 * 60 * 1000, - enabled: isAuthenticated && hasValidToken, + enabled: isAuthenticated && !!tokens?.accessToken, }); } @@ -110,7 +110,7 @@ export function useSubscriptionStats() { * Hook to fetch a specific subscription */ export function useSubscription(subscriptionId: number) { - const { isAuthenticated, hasValidToken } = useAuthSession(); + const { isAuthenticated, tokens } = useAuthStore(); return useQuery({ queryKey: queryKeys.subscriptions.detail(String(subscriptionId)), @@ -122,7 +122,7 @@ export function useSubscription(subscriptionId: number) { }, staleTime: 5 * 60 * 1000, gcTime: 10 * 60 * 1000, - enabled: isAuthenticated && hasValidToken && subscriptionId > 0, + enabled: isAuthenticated && !!tokens?.accessToken && subscriptionId > 0, }); } @@ -134,7 +134,7 @@ export function useSubscriptionInvoices( options: { page?: number; limit?: number } = {} ) { const { page = 1, limit = 10 } = options; - const { isAuthenticated, hasValidToken } = useAuthSession(); + const { isAuthenticated, tokens } = useAuthStore(); return useQuery({ queryKey: queryKeys.subscriptions.invoices(subscriptionId, { page, limit }), @@ -155,6 +155,6 @@ export function useSubscriptionInvoices( }, staleTime: 60 * 1000, gcTime: 5 * 60 * 1000, - enabled: isAuthenticated && hasValidToken && subscriptionId > 0, + enabled: isAuthenticated && !!tokens?.accessToken && subscriptionId > 0, }); } diff --git a/apps/portal/src/features/subscriptions/services/sim-actions.service.ts b/apps/portal/src/features/subscriptions/services/sim-actions.service.ts new file mode 100644 index 00000000..7bab0cdf --- /dev/null +++ b/apps/portal/src/features/subscriptions/services/sim-actions.service.ts @@ -0,0 +1,49 @@ +import { apiClient, getDataOrDefault } from "@/lib/api"; + +export interface TopUpRequest { + quotaMb: number; +} + +export interface ChangePlanRequest { + newPlanCode: string; + assignGlobalIp: boolean; +} + +export interface CancelRequest { + scheduledAt: string; +} + +export interface SimInfo { + details: T; + error?: E; +} + +export const simActionsService = { + async topUp(subscriptionId: string, request: TopUpRequest): Promise { + await apiClient.POST("/api/subscriptions/{subscriptionId}/sim/top-up", { + params: { path: { subscriptionId } }, + body: request, + }); + }, + + async changePlan(subscriptionId: string, request: ChangePlanRequest): Promise { + await apiClient.POST("/api/subscriptions/{subscriptionId}/sim/change-plan", { + params: { path: { subscriptionId } }, + body: request, + }); + }, + + async cancel(subscriptionId: string, request: CancelRequest): Promise { + await apiClient.POST("/api/subscriptions/{subscriptionId}/sim/cancel", { + params: { path: { subscriptionId } }, + body: request, + }); + }, + + async getSimInfo(subscriptionId: string): Promise | null> { + const response = await apiClient.GET("/api/subscriptions/{subscriptionId}/sim/info", { + params: { path: { subscriptionId } }, + }); + return getDataOrDefault(response, null); + }, +}; diff --git a/apps/portal/src/lib/api/index.ts b/apps/portal/src/lib/api/index.ts index 1e85e4b2..5b997d33 100644 --- a/apps/portal/src/lib/api/index.ts +++ b/apps/portal/src/lib/api/index.ts @@ -28,7 +28,11 @@ export const queryKeys = { }, subscriptions: { all: () => ['subscriptions'] as const, - list: () => ['subscriptions', 'list'] as const, + list: (params?: Record) => ['subscriptions', 'list', params] as const, + active: () => ['subscriptions', 'active'] as const, + stats: () => ['subscriptions', 'stats'] as const, + detail: (id: string) => ['subscriptions', 'detail', id] as const, + invoices: (id: number, params?: Record) => ['subscriptions', 'invoices', id, params] as const, }, dashboard: { summary: () => ['dashboard', 'summary'] as const, diff --git a/apps/portal/src/lib/validation/index.ts b/apps/portal/src/lib/validation/index.ts index a8c86c4f..ce9f91f7 100644 --- a/apps/portal/src/lib/validation/index.ts +++ b/apps/portal/src/lib/validation/index.ts @@ -10,8 +10,8 @@ export type { ZodFormOptions } from './zod-form'; // Re-export Zod for convenience export { z } from 'zod'; -// Re-export shared validation schemas -export * from '@customer-portal/validation'; +// Re-export shared validation schemas (React-specific only) +export * from '@customer-portal/validation/react'; // Additional React-specific form utilities export type { UseZodFormReturn } from './zod-form';