Enhance SIM management features and improve UI responsiveness
- Introduced SimVoiceOptions model to manage voice-related settings for accounts. - Added debug methods in SimDetailsService and SimOrchestratorService to fetch SIM details directly from Freebit, bypassing subscription validation for troubleshooting. - Updated SimManagementSection and SubscriptionDetail components for improved UI with backdrop blur effects and enhanced layout. - Integrated AuroraBackground component for a visually appealing background in SubscriptionDetail, enhancing user experience.
This commit is contained in:
parent
9f9a1897ee
commit
5ad4089d4e
@ -168,3 +168,15 @@ model SimUsageDaily {
|
|||||||
@@index([account, date])
|
@@index([account, date])
|
||||||
@@map("sim_usage_daily")
|
@@map("sim_usage_daily")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model SimVoiceOptions {
|
||||||
|
account String @id
|
||||||
|
voiceMailEnabled Boolean @default(false) @map("voice_mail_enabled")
|
||||||
|
callWaitingEnabled Boolean @default(false) @map("call_waiting_enabled")
|
||||||
|
internationalRoamingEnabled Boolean @default(false) @map("international_roaming_enabled")
|
||||||
|
networkType String @default("4G") @map("network_type")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
@@map("sim_voice_options")
|
||||||
|
}
|
||||||
|
|||||||
@ -5,14 +5,13 @@ import type {
|
|||||||
SimDetails,
|
SimDetails,
|
||||||
SimUsage,
|
SimUsage,
|
||||||
SimTopUpHistory,
|
SimTopUpHistory,
|
||||||
} from "@bff/integrations/freebit/interfaces/freebit.types";
|
|
||||||
import type {
|
|
||||||
SimTopUpRequest,
|
SimTopUpRequest,
|
||||||
SimPlanChangeRequest,
|
SimPlanChangeRequest,
|
||||||
SimCancelRequest,
|
SimCancelRequest,
|
||||||
SimTopUpHistoryRequest,
|
SimTopUpHistoryRequest,
|
||||||
SimFeaturesUpdateRequest,
|
SimFeaturesUpdateRequest,
|
||||||
} from "./sim-management/types/sim-requests.types";
|
SimReissueRequest,
|
||||||
|
} from "@customer-portal/domain/sim";
|
||||||
import type { SimNotificationContext } from "./sim-management/interfaces/sim-base.interface";
|
import type { SimNotificationContext } from "./sim-management/interfaces/sim-base.interface";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -120,8 +119,13 @@ export class SimManagementService {
|
|||||||
/**
|
/**
|
||||||
* Reissue eSIM profile
|
* Reissue eSIM profile
|
||||||
*/
|
*/
|
||||||
async reissueEsimProfile(userId: string, subscriptionId: number, newEid?: string): Promise<void> {
|
async reissueEsimProfile(
|
||||||
return this.simOrchestrator.reissueEsimProfile(userId, subscriptionId, newEid);
|
userId: string,
|
||||||
|
subscriptionId: number,
|
||||||
|
newEid?: string
|
||||||
|
): Promise<void> {
|
||||||
|
const request: SimReissueRequest = newEid ? { newEid } : {};
|
||||||
|
return this.simOrchestrator.reissueEsimProfile(userId, subscriptionId, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -40,4 +40,33 @@ export class SimDetailsService {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get SIM details directly from Freebit without subscription validation
|
||||||
|
* Useful for debugging specific accounts
|
||||||
|
*/
|
||||||
|
async getSimDetailsDirectly(account: string): Promise<SimDetails> {
|
||||||
|
try {
|
||||||
|
this.logger.log(`[DEBUG] Querying Freebit for account: ${account}`);
|
||||||
|
|
||||||
|
const simDetails = await this.freebitService.getSimDetails(account);
|
||||||
|
|
||||||
|
this.logger.log(`[DEBUG] Retrieved SIM details from Freebit`, {
|
||||||
|
account,
|
||||||
|
planCode: simDetails.planCode,
|
||||||
|
planName: simDetails.planName,
|
||||||
|
status: simDetails.status,
|
||||||
|
simType: simDetails.simType,
|
||||||
|
});
|
||||||
|
|
||||||
|
return simDetails;
|
||||||
|
} catch (error) {
|
||||||
|
const sanitizedError = getErrorMessage(error);
|
||||||
|
this.logger.error(`[DEBUG] Failed to get SIM details for account ${account}`, {
|
||||||
|
error: sanitizedError,
|
||||||
|
account,
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -157,4 +157,12 @@ export class SimOrchestratorService {
|
|||||||
): Promise<Record<string, unknown>> {
|
): Promise<Record<string, unknown>> {
|
||||||
return this.simValidation.debugSimSubscription(userId, subscriptionId);
|
return this.simValidation.debugSimSubscription(userId, subscriptionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug method to query Freebit directly for any account's details
|
||||||
|
* Bypasses subscription validation and queries Freebit directly
|
||||||
|
*/
|
||||||
|
async getSimDetailsDirectly(account: string): Promise<SimDetails> {
|
||||||
|
return this.simDetails.getSimDetailsDirectly(account);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,9 @@
|
|||||||
@import "../styles/tokens.css";
|
@import "../styles/tokens.css";
|
||||||
@import "../styles/utilities.css";
|
@import "../styles/utilities.css";
|
||||||
@import "../styles/responsive.css";
|
@import "../styles/responsive.css";
|
||||||
|
@import "tw-animate-css";
|
||||||
|
|
||||||
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--radius: 0.625rem;
|
--radius: 0.625rem;
|
||||||
@ -217,3 +220,24 @@
|
|||||||
font-family: var(--font-geist-sans), system-ui, sans-serif;
|
font-family: var(--font-geist-sans), system-ui, sans-serif;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@theme inline {
|
||||||
|
--animate-aurora: aurora 60s linear infinite;
|
||||||
|
@keyframes aurora {
|
||||||
|
from {
|
||||||
|
backgroundPosition: 50% 50%, 50% 50%;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
backgroundPosition: 350% 50%, 350% 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border outline-ring/50;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import React, { ReactNode } from "react";
|
||||||
|
|
||||||
|
interface AuroraBackgroundProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
|
children: ReactNode;
|
||||||
|
showRadialGradient?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AuroraBackground = ({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
showRadialGradient = true,
|
||||||
|
...props
|
||||||
|
}: AuroraBackgroundProps) => {
|
||||||
|
return (
|
||||||
|
<main>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"transition-bg relative flex h-[100vh] flex-col items-center justify-center bg-zinc-50 text-slate-950 dark:bg-zinc-900",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 overflow-hidden"
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--aurora":
|
||||||
|
"repeating-linear-gradient(100deg,#3b82f6_10%,#a5b4fc_15%,#93c5fd_20%,#ddd6fe_25%,#60a5fa_30%)",
|
||||||
|
"--dark-gradient":
|
||||||
|
"repeating-linear-gradient(100deg,#000_0%,#000_7%,transparent_10%,transparent_12%,#000_16%)",
|
||||||
|
"--white-gradient":
|
||||||
|
"repeating-linear-gradient(100deg,#fff_0%,#fff_7%,transparent_10%,transparent_12%,#fff_16%)",
|
||||||
|
|
||||||
|
"--blue-300": "#93c5fd",
|
||||||
|
"--blue-400": "#60a5fa",
|
||||||
|
"--blue-500": "#3b82f6",
|
||||||
|
"--indigo-300": "#a5b4fc",
|
||||||
|
"--violet-200": "#ddd6fe",
|
||||||
|
"--black": "#000",
|
||||||
|
"--white": "#fff",
|
||||||
|
"--transparent": "transparent",
|
||||||
|
} as React.CSSProperties
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
// I'm sorry but this is what peak developer performance looks like // trigger warning
|
||||||
|
className={cn(
|
||||||
|
`after:animate-aurora pointer-events-none absolute -inset-[10px] [background-image:var(--white-gradient),var(--aurora)] [background-size:300%,_200%] [background-position:50%_50%,50%_50%] opacity-50 blur-[10px] invert filter will-change-transform [--aurora:repeating-linear-gradient(100deg,var(--blue-500)_10%,var(--indigo-300)_15%,var(--blue-300)_20%,var(--violet-200)_25%,var(--blue-400)_30%)] [--dark-gradient:repeating-linear-gradient(100deg,var(--black)_0%,var(--black)_7%,var(--transparent)_10%,var(--transparent)_12%,var(--black)_16%)] [--white-gradient:repeating-linear-gradient(100deg,var(--white)_0%,var(--white)_7%,var(--transparent)_10%,var(--transparent)_12%,var(--white)_16%)] after:absolute after:inset-0 after:[background-image:var(--white-gradient),var(--aurora)] after:[background-size:200%,_100%] after:[background-attachment:fixed] after:mix-blend-difference after:content-[""] dark:[background-image:var(--dark-gradient),var(--aurora)] dark:invert-0 after:dark:[background-image:var(--dark-gradient),var(--aurora)]`,
|
||||||
|
|
||||||
|
showRadialGradient &&
|
||||||
|
`[mask-image:radial-gradient(ellipse_at_100%_0%,black_10%,var(--transparent)_70%)]`,
|
||||||
|
)}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -137,7 +137,7 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
|
|||||||
return (
|
return (
|
||||||
<div id="sim-management" className="space-y-6">
|
<div id="sim-management" className="space-y-6">
|
||||||
{/* Header Section */}
|
{/* Header Section */}
|
||||||
<div className="bg-white shadow-sm rounded-lg border border-gray-200 p-6">
|
<div className="bg-white/90 backdrop-blur-sm shadow-sm rounded-lg border border-gray-200 p-6">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold text-gray-900">
|
<h1 className="text-2xl font-bold text-gray-900">
|
||||||
@ -156,7 +156,7 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
|
|||||||
{/* Left Column - Main Actions */}
|
{/* Left Column - Main Actions */}
|
||||||
<div className="lg:col-span-2 space-y-6">
|
<div className="lg:col-span-2 space-y-6">
|
||||||
{/* Subscription Details Card */}
|
{/* Subscription Details Card */}
|
||||||
<div className="bg-white shadow-sm rounded-lg border border-gray-200 p-6">
|
<div className="bg-white/90 backdrop-blur-sm shadow-sm rounded-lg border border-gray-200 p-6">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h2 className="text-lg font-semibold text-gray-900">Subscription Details</h2>
|
<h2 className="text-lg font-semibold text-gray-900">Subscription Details</h2>
|
||||||
</div>
|
</div>
|
||||||
@ -177,7 +177,7 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* SIM Management Actions Card */}
|
{/* SIM Management Actions Card */}
|
||||||
<div className="bg-white shadow-sm rounded-lg border border-gray-200 p-6">
|
<div className="bg-white/90 backdrop-blur-sm shadow-sm rounded-lg border border-gray-200 p-6">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h2 className="text-lg font-semibold text-gray-900">SIM Management Actions</h2>
|
<h2 className="text-lg font-semibold text-gray-900">SIM Management Actions</h2>
|
||||||
</div>
|
</div>
|
||||||
@ -195,7 +195,7 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Voice Status Card */}
|
{/* Voice Status Card */}
|
||||||
<div className="bg-white shadow-sm rounded-lg border border-gray-200 p-6">
|
<div className="bg-white/90 backdrop-blur-sm shadow-sm rounded-lg border border-gray-200 p-6">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h2 className="text-lg font-semibold text-gray-900">Voice Status</h2>
|
<h2 className="text-lg font-semibold text-gray-900">Voice Status</h2>
|
||||||
</div>
|
</div>
|
||||||
@ -214,7 +214,7 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
|
|||||||
{/* Right Column - SIM Details & Usage */}
|
{/* Right Column - SIM Details & Usage */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* SIM Details Card */}
|
{/* SIM Details Card */}
|
||||||
<div className="bg-white shadow-sm rounded-lg border border-gray-200 p-6">
|
<div className="bg-white/90 backdrop-blur-sm shadow-sm rounded-lg border border-gray-200 p-6">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h2 className="text-lg font-semibold text-gray-900">SIM Details</h2>
|
<h2 className="text-lg font-semibold text-gray-900">SIM Details</h2>
|
||||||
</div>
|
</div>
|
||||||
@ -228,7 +228,6 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import {
|
|||||||
getSubscriptionStatusIcon,
|
getSubscriptionStatusIcon,
|
||||||
getSubscriptionStatusVariant,
|
getSubscriptionStatusVariant,
|
||||||
} from "@/features/subscriptions/utils/status-presenters";
|
} from "@/features/subscriptions/utils/status-presenters";
|
||||||
|
import { AuroraBackground } from "@/components/ui/shadcn-io/aurora-background";
|
||||||
|
|
||||||
export function SubscriptionDetailContainer() {
|
export function SubscriptionDetailContainer() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@ -115,8 +116,13 @@ export function SubscriptionDetailContainer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-6">
|
<div className="relative min-h-screen">
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
|
<AuroraBackground className="fixed inset-0 -z-10" showRadialGradient={false}>
|
||||||
|
<div className="h-full" />
|
||||||
|
</AuroraBackground>
|
||||||
|
|
||||||
|
<div className="relative z-10 py-6">
|
||||||
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
@ -134,7 +140,7 @@ export function SubscriptionDetailContainer() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SubCard className="mb-6">
|
<SubCard className="mb-6 bg-white/90 backdrop-blur-sm">
|
||||||
<DetailHeader
|
<DetailHeader
|
||||||
title="Subscription Details"
|
title="Subscription Details"
|
||||||
subtitle="Service subscription information"
|
subtitle="Service subscription information"
|
||||||
@ -187,7 +193,7 @@ export function SubscriptionDetailContainer() {
|
|||||||
|
|
||||||
{subscription.productName.toLowerCase().includes("sim") && (
|
{subscription.productName.toLowerCase().includes("sim") && (
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<SubCard>
|
<SubCard className="bg-white/90 backdrop-blur-sm">
|
||||||
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between space-y-4 lg:space-y-0">
|
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between space-y-4 lg:space-y-0">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-xl font-semibold text-gray-900">Service Management</h3>
|
<h3 className="text-xl font-semibold text-gray-900">Service Management</h3>
|
||||||
@ -223,6 +229,7 @@ export function SubscriptionDetailContainer() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{showInvoices && <InvoicesList subscriptionId={subscriptionId} pageSize={5} />}
|
{showInvoices && <InvoicesList subscriptionId={subscriptionId} pageSize={5} />}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user