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 # Generate Prisma client in production environment
WORKDIR /app/apps/bff WORKDIR /app/apps/bff
RUN pnpm dlx prisma generate RUN pnpm dlx prisma@6.14.0 generate
# Strip build toolchain to shrink image # Strip build toolchain to shrink image
RUN apk del --no-cache python3 make g++ pkgconfig && rm -rf /root/.cache /var/cache/apk/* RUN apk del --no-cache python3 make g++ pkgconfig && rm -rf /root/.cache /var/cache/apk/*

View File

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

View File

@ -223,10 +223,10 @@ export function SimManagementSection({ subscriptionId }: SimManagementSectionPro
</p> </p>
<button <button
onClick={handlePayInvoice} 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" 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> </button>
</div> </div>
)} )}

View File

@ -4,6 +4,7 @@ import type {
SimTopUpRequest, SimTopUpRequest,
SimPlanChangeRequest, SimPlanChangeRequest,
SimCancelRequest, SimCancelRequest,
SimReissueRequest,
} from "@customer-portal/domain/sim"; } from "@customer-portal/domain/sim";
// Types imported from domain - no duplication // Types imported from domain - no duplication
@ -45,4 +46,11 @@ export const simActionsService = {
return simInfoSchema.parse(response.data); 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 SalesforceProductFieldMap,
type PricingTier, type PricingTier,
type CatalogFilter, type CatalogFilter,
type CatalogPriceInfo,
} from "./contract"; } from "./contract";
// Schemas (includes derived types) // Schemas (includes derived types)