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:
tema 2025-11-22 18:11:43 +09:00
parent 9f9a1897ee
commit 5ad4089d4e
8 changed files with 160 additions and 15 deletions

View File

@ -168,3 +168,15 @@ model SimUsageDaily {
@@index([account, date])
@@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")
}

View File

@ -5,14 +5,13 @@ import type {
SimDetails,
SimUsage,
SimTopUpHistory,
} from "@bff/integrations/freebit/interfaces/freebit.types";
import type {
SimTopUpRequest,
SimPlanChangeRequest,
SimCancelRequest,
SimTopUpHistoryRequest,
SimFeaturesUpdateRequest,
} from "./sim-management/types/sim-requests.types";
SimReissueRequest,
} from "@customer-portal/domain/sim";
import type { SimNotificationContext } from "./sim-management/interfaces/sim-base.interface";
@Injectable()
@ -120,8 +119,13 @@ export class SimManagementService {
/**
* Reissue eSIM profile
*/
async reissueEsimProfile(userId: string, subscriptionId: number, newEid?: string): Promise<void> {
return this.simOrchestrator.reissueEsimProfile(userId, subscriptionId, newEid);
async reissueEsimProfile(
userId: string,
subscriptionId: number,
newEid?: string
): Promise<void> {
const request: SimReissueRequest = newEid ? { newEid } : {};
return this.simOrchestrator.reissueEsimProfile(userId, subscriptionId, request);
}
/**

View File

@ -40,4 +40,33 @@ export class SimDetailsService {
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;
}
}
}

View File

@ -157,4 +157,12 @@ export class SimOrchestratorService {
): Promise<Record<string, unknown>> {
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);
}
}

View File

@ -3,6 +3,9 @@
@import "../styles/tokens.css";
@import "../styles/utilities.css";
@import "../styles/responsive.css";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
:root {
--radius: 0.625rem;
@ -217,3 +220,24 @@
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;
}
}

View File

@ -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>
);
};

View File

@ -137,7 +137,7 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
return (
<div id="sim-management" className="space-y-6">
{/* 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>
<h1 className="text-2xl font-bold text-gray-900">
@ -156,7 +156,7 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
{/* Left Column - Main Actions */}
<div className="lg:col-span-2 space-y-6">
{/* 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">
<h2 className="text-lg font-semibold text-gray-900">Subscription Details</h2>
</div>
@ -177,7 +177,7 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
</div>
{/* 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">
<h2 className="text-lg font-semibold text-gray-900">SIM Management Actions</h2>
</div>
@ -195,7 +195,7 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
</div>
{/* 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">
<h2 className="text-lg font-semibold text-gray-900">Voice Status</h2>
</div>
@ -214,7 +214,7 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
{/* Right Column - SIM Details & Usage */}
<div className="space-y-6">
{/* 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">
<h2 className="text-lg font-semibold text-gray-900">SIM Details</h2>
</div>
@ -228,7 +228,6 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
</div>
</div>
</div>
</div>
);
}

View File

@ -25,6 +25,7 @@ import {
getSubscriptionStatusIcon,
getSubscriptionStatusVariant,
} from "@/features/subscriptions/utils/status-presenters";
import { AuroraBackground } from "@/components/ui/shadcn-io/aurora-background";
export function SubscriptionDetailContainer() {
const params = useParams();
@ -115,8 +116,13 @@ export function SubscriptionDetailContainer() {
}
return (
<div className="py-6">
<div className="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<div className="relative min-h-screen">
<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="flex items-center justify-between">
<div className="flex items-center">
@ -134,7 +140,7 @@ export function SubscriptionDetailContainer() {
</div>
</div>
<SubCard className="mb-6">
<SubCard className="mb-6 bg-white/90 backdrop-blur-sm">
<DetailHeader
title="Subscription Details"
subtitle="Service subscription information"
@ -187,7 +193,7 @@ export function SubscriptionDetailContainer() {
{subscription.productName.toLowerCase().includes("sim") && (
<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>
<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} />}
</div>
</div>
</div>
);