Update Dockerfile and SimDetailsCard component for compatibility and enhancements

- Updated Prisma client generation command in Dockerfile to use version 6.14.0 for consistency.
- Refactored SimDetailsCard component to improve type handling and UI elements, including re-exporting SimDetails for backwards compatibility.
- Adjusted display logic for SIM details, including changes to how plan types and service dates are presented.
- Enhanced button states in SimManagementSection for better clarity on invoice payment status.
- Added reissueEsim method in sim-actions.service for handling eSIM reissue requests.
- Introduced CatalogPriceInfo type in domain catalog for improved type definitions.
This commit is contained in:
barsa 2025-12-01 10:07:58 +09:00
parent 9fb54fd0fc
commit c397eb51c7
5 changed files with 42 additions and 70 deletions

View File

@ -106,7 +106,7 @@ COPY --from=builder /app/apps/bff/prisma ./apps/bff/prisma
# Generate Prisma client in production environment
WORKDIR /app/apps/bff
RUN pnpm dlx prisma generate
RUN pnpm dlx prisma@6.14.0 generate
# Strip build toolchain to shrink image
RUN apk del --no-cache python3 make g++ pkgconfig && rm -rf /root/.cache /var/cache/apk/*

View File

@ -10,6 +10,10 @@ import {
ExclamationTriangleIcon,
XCircleIcon,
} from "@heroicons/react/24/outline";
import type { SimDetails } from "@customer-portal/domain/sim";
// Re-export for backwards compatibility
export type { SimDetails };
// Inline formatPlanShort function
function formatPlanShort(planCode?: string): string {
@ -26,33 +30,6 @@ function formatPlanShort(planCode?: string): string {
return planCode;
}
export interface SimDetails {
account: string;
msisdn: string;
iccid?: string;
imsi?: string;
eid?: string;
planCode: string;
status: "active" | "suspended" | "cancelled" | "pending";
simType: "physical" | "esim";
size: "standard" | "nano" | "micro" | "esim";
hasVoice: boolean;
hasSms: boolean;
remainingQuotaKb: number;
remainingQuotaMb: number;
startDate?: string;
ipv4?: string;
ipv6?: string;
voiceMailEnabled?: boolean;
callWaitingEnabled?: boolean;
internationalRoamingEnabled?: boolean;
networkType?: string;
pendingOperations?: Array<{
operation: string;
scheduledDate: string;
}>;
}
interface SimDetailsCardProps {
simDetails: SimDetails;
isLoading?: boolean;
@ -292,7 +269,7 @@ export function SimDetailsCard({
<div>
<h3 className="text-lg font-medium text-gray-900">Physical SIM Details</h3>
<p className="text-sm text-gray-500">
{formatPlan(simDetails.planCode)} {`${simDetails.size} SIM`}
{formatPlan(simDetails.planCode)} {`${simDetails.simType} SIM`}
</p>
</div>
</div>
@ -321,12 +298,10 @@ export function SimDetailsCard({
<p className="text-sm font-medium text-gray-900">{simDetails.msisdn}</p>
</div>
{simDetails.simType === "physical" && (
<div>
<label className="text-xs text-gray-500">ICCID</label>
<p className="text-sm font-mono text-gray-900 break-all">{simDetails.iccid}</p>
</div>
)}
<div>
<label className="text-xs text-gray-500">ICCID</label>
<p className="text-sm font-mono text-gray-900 break-all">{simDetails.iccid}</p>
</div>
{simDetails.eid && (
<div>
@ -342,10 +317,10 @@ export function SimDetailsCard({
</div>
)}
{simDetails.startDate && (
{simDetails.activatedAt && (
<div>
<label className="text-xs text-gray-500">Service Start Date</label>
<p className="text-sm text-gray-900">{formatDate(simDetails.startDate)}</p>
<p className="text-sm text-gray-900">{formatDate(simDetails.activatedAt)}</p>
</div>
)}
</div>
@ -368,37 +343,32 @@ export function SimDetailsCard({
<div className="flex items-center space-x-4">
<div className="flex items-center">
<SignalIcon
className={`h-4 w-4 mr-1 ${simDetails.hasVoice ? "text-green-500" : "text-gray-400"}`}
className={`h-4 w-4 mr-1 ${simDetails.voiceMailEnabled ? "text-green-500" : "text-gray-400"}`}
/>
<span
className={`text-sm ${simDetails.hasVoice ? "text-green-600" : "text-gray-500"}`}
className={`text-sm ${simDetails.voiceMailEnabled ? "text-green-600" : "text-gray-500"}`}
>
Voice {simDetails.hasVoice ? "Enabled" : "Disabled"}
Voicemail {simDetails.voiceMailEnabled ? "Enabled" : "Disabled"}
</span>
</div>
<div className="flex items-center">
<DevicePhoneMobileIcon
className={`h-4 w-4 mr-1 ${simDetails.hasSms ? "text-green-500" : "text-gray-400"}`}
className={`h-4 w-4 mr-1 ${simDetails.callWaitingEnabled ? "text-green-500" : "text-gray-400"}`}
/>
<span
className={`text-sm ${simDetails.hasSms ? "text-green-600" : "text-gray-500"}`}
className={`text-sm ${simDetails.callWaitingEnabled ? "text-green-600" : "text-gray-500"}`}
>
SMS {simDetails.hasSms ? "Enabled" : "Disabled"}
Call Waiting {simDetails.callWaitingEnabled ? "Enabled" : "Disabled"}
</span>
</div>
</div>
{(simDetails.ipv4 || simDetails.ipv6) && (
<div>
<label className="text-xs text-gray-500">IP Address</label>
<div className="space-y-1">
{simDetails.ipv4 && (
<p className="text-sm font-mono text-gray-900">IPv4: {simDetails.ipv4}</p>
)}
{simDetails.ipv6 && (
<p className="text-sm font-mono text-gray-900">IPv6: {simDetails.ipv6}</p>
)}
</div>
{simDetails.internationalRoamingEnabled && (
<div className="flex items-center">
<WifiIcon className="h-4 w-4 mr-1 text-green-500" />
<span className="text-sm text-green-600">
International Roaming Enabled
</span>
</div>
)}
</div>
@ -406,21 +376,14 @@ export function SimDetailsCard({
)}
</div>
{/* Pending Operations */}
{simDetails.pendingOperations && simDetails.pendingOperations.length > 0 && (
{/* Expiry Date */}
{simDetails.expiresAt && (
<div className="mt-6 pt-6 border-t border-gray-200">
<h4 className="text-sm font-medium text-gray-500 uppercase tracking-wider mb-3">
Pending Operations
</h4>
<div className="bg-blue-50 rounded-lg p-4">
{simDetails.pendingOperations.map((operation, index) => (
<div key={index} className="flex items-center text-sm">
<ClockIcon className="h-4 w-4 text-blue-500 mr-2" />
<span className="text-blue-800">
{operation.operation} scheduled for {formatDate(operation.scheduledDate)}
</span>
</div>
))}
<div className="flex items-center text-sm">
<ClockIcon className="h-4 w-4 text-amber-500 mr-2" />
<span className="text-amber-800">
Expires on {formatDate(simDetails.expiresAt)}
</span>
</div>
</div>
)}

View File

@ -223,10 +223,10 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
</p>
<button
onClick={handlePayInvoice}
disabled={createSsoLink.isPending || latestInvoice.status === 'paid'}
disabled={createSsoLink.isPending || latestInvoice.status === 'Paid'}
className="w-full px-4 py-2 bg-blue-600 text-white text-sm font-semibold rounded-lg hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors"
>
{latestInvoice.status === 'paid' ? 'PAID' : createSsoLink.isPending ? 'Loading...' : 'PAY'}
{latestInvoice.status === 'Paid' ? 'PAID' : createSsoLink.isPending ? 'Loading...' : 'PAY'}
</button>
</div>
)}

View File

@ -4,6 +4,7 @@ import type {
SimTopUpRequest,
SimPlanChangeRequest,
SimCancelRequest,
SimReissueRequest,
} from "@customer-portal/domain/sim";
// Types imported from domain - no duplication
@ -45,4 +46,11 @@ export const simActionsService = {
return simInfoSchema.parse(response.data);
},
async reissueEsim(subscriptionId: string, request: SimReissueRequest): Promise<void> {
await apiClient.POST("/api/subscriptions/{subscriptionId}/sim/reissue-esim", {
params: { path: { subscriptionId } },
body: request,
});
},
};

View File

@ -11,6 +11,7 @@ export {
type SalesforceProductFieldMap,
type PricingTier,
type CatalogFilter,
type CatalogPriceInfo,
} from "./contract";
// Schemas (includes derived types)