diff --git a/app/apollo/apollo-octopus-public/src/commonMain/graphql/com/hedvig/android/apollo/octopus/graphql/FragmentPartnerClaimFragment.graphql b/app/apollo/apollo-octopus-public/src/commonMain/graphql/com/hedvig/android/apollo/octopus/graphql/FragmentPartnerClaimFragment.graphql new file mode 100644 index 0000000000..fa6fb0a00f --- /dev/null +++ b/app/apollo/apollo-octopus-public/src/commonMain/graphql/com/hedvig/android/apollo/octopus/graphql/FragmentPartnerClaimFragment.graphql @@ -0,0 +1,25 @@ +fragment PartnerClaimFragment on PartnerClaim { + id + externalId + exposureDisplayName + status + submittedAt + payoutAmount { + ...MoneyFragment + } + associatedTypeOfContract + claimType + handlerEmail + displayItems { + displayTitle + displayValue + } + productVariant { + typeOfContract + displayName + documents { + type + url + } + } +} diff --git a/app/apollo/apollo-octopus-public/src/commonMain/graphql/com/hedvig/android/apollo/octopus/schema.graphqls b/app/apollo/apollo-octopus-public/src/commonMain/graphql/com/hedvig/android/apollo/octopus/schema.graphqls index 288983627b..eaf47adc1a 100644 --- a/app/apollo/apollo-octopus-public/src/commonMain/graphql/com/hedvig/android/apollo/octopus/schema.graphqls +++ b/app/apollo/apollo-octopus-public/src/commonMain/graphql/com/hedvig/android/apollo/octopus/schema.graphqls @@ -468,6 +468,41 @@ type BundleYearlySavings { """ bundleDiscountCoversFullPeriod: Boolean! } +""" +SHA-256-hashed user data for Facebook Conversions API (CAPI). +All non-null field values are hex-encoded SHA-256 hashes of the normalized plaintext. +Normalization follows https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters +""" +type CapiUserData { + """ + Hashed email (em) + """ + em: String! + """ + Hashed Gmail-normalized email, only present for Gmail addresses (gem) + """ + gem: String + """ + Hashed first name (fn) + """ + fn: String! + """ + Hashed last name (ln) + """ + ln: String! + """ + Hashed postal/zip code, null if unavailable (zp) + """ + zp: String + """ + Hashed city, null if unavailable (ct) + """ + ct: String + """ + Hashed country code, e.g. 'se' (co) + """ + co: String! +} type CarItemNotification { message: String! } @@ -1687,15 +1722,15 @@ returned as ExtendedItemDiscount """ type ExtendedItemDiscount { """ - General discount information + General discount information """ itemDiscount: ItemDiscount! """ - Monthly reduction applied by the discount. It's a negative number + Monthly reduction applied by the discount. It's a negative number """ amount: Money! """ - Whether discount is on a pending state or not + Whether discount is on a pending state or not """ isPending: Boolean! } @@ -1728,6 +1763,11 @@ type ExternalInsurer { displayName: String! insurelyId: String } +type FetchedExternalInsurance { + displayName: String! + subtitle: String + insurer: ExternalInsurer! +} type FirstVetAction { sections: [FirstVetSection!]! } @@ -2419,6 +2459,8 @@ type Member { claims: [Claim!]! claimsActive: [Claim!]! claimsHistory: [Claim!]! + partnerClaimsActive: [PartnerClaim!]! + partnerClaimsHistory: [PartnerClaim!]! firstName: String! lastName: String! ssn: String @@ -2477,6 +2519,11 @@ type Member { """ crossSellV2(input: CrossSellInput!): CrossSellV2! """ + Young Pet Guide stories for the member. + Returns a list of educational content stories for young pet owners. + """ + puppyGuideStories: [PuppyGuideStory!]! + """ Fetch all the active contracts for this member. Active contracts include all insurances that are either active today, or to-be-active in the future. """ @@ -2729,6 +2776,22 @@ type MemberPaymentAvailablePaymentMethod { True if the member can set up this payment method for payout. """ supportsPayout: Boolean! + """ + True if this method is already ACTIVE for member and can be chosen as default directly without setup, false if + this is a new payment method that the member has not yet set up. + If this is true, then the `details` field will be populated with the payment method details. If this is false, then + the `details` field will be null since the member has not yet set up this payment method. + If true then this method can be set up as default directly by calling `paymentMethodSetDefaultPayin` or + `paymentMethodSetDefaultPayout` mutation depending on if it's a payin or payout method. If false, then the + corresponding mutation for setting up this payment method should be called, eg. `paymentMethodSetupTrustly`, + `paymentMethodSetupSwishPayin` etc. + """ + isActive: Boolean! + """ + For already connected and ACTIVE methods, ie isActive=true, specific details of the actual connection - e.g. a bank + account reference, phone number for swish, or email/kivra for invoice. + """ + details: PaymentMethodDetails } type MemberPaymentChargeMethodInfo { """ @@ -2801,48 +2864,76 @@ type MemberPaymentInformation { } type MemberPaymentMethod { """ - The unique id of the payment method. This id is used for switching default and revoking payment methods. - """ - id: ID! - """ - Payment provider, eg Trustly, Swish, Nordea, Kivra etc. + Payment provider, eg Trustly, Swish, Nordea, Kivra etc. + This is used as the "identifier" of the payment method since there can only be one ACTIVE or PENDING payment method + per provider. """ provider: MemberPaymentProvider! """ - The payment method status - ACTIVE, PENDING, or PENDING_DEFAULT. - PENDING_DEFAULT means the payment method is awaiting activation and will become default once activated. + The payment method status - ACTIVE, PENDING. + If ACTIVE, the payment method is ready to use for payins or payouts depending on if it's a payin or payout method. + If PENDING, the payment method has been set up but is still awaiting activation and cannot be used for payins or + payouts until then. Once activated, the status will change to ACTIVE. """ status: MemberPaymentMethodStatus! """ - True if this is the default payment method. Only one ACTIVE payment method can be default at a time. - If status is PENDING then payment method will become the default once activated. + This is 'true' for only one of the members ACTIVE methods which is the default payment method that will be used for + charging or payout the member. For PENDING methods, this can also be 'true' if the member has chosen to set up this + payment method as default during the setup process. """ isDefault: Boolean! """ - Specific details of the actual connection - e.g. a bank account reference, phone number for swish, - or email/kivra for invoice. + Specific details of the actual connection if method is ACTIVE - e.g. a bank account reference, phone number for swish, + or email/kivra for invoice. If method is PENDING, then this field will be null since the member has not yet set up + this payment method. """ - details: PaymentMethodDetails! + details: PaymentMethodDetails } type MemberPaymentMethods { """ - List of active and pending payment payin methods for this member. + List of all member's ACTIVE and PENDING payment payin methods. + A member can have multiple ACTIVE payment methods with these constraints: + - Only one ACTIVE payment method per provider, eg. one Trustly, one Swish, one Nordea etc. + - Only one ACTIVE payment method can be default at a time. + A member can have multiple PENDING payment methods with these constraints: + - Only one PENDING payment method per provider, eg. one Trustly, one Swish, one Nordea etc. + So there can exist max two payment methods per provider, one ACTIVE and one PENDING. + If a PENDING payment method has isDefault=true, then it will become the default ACTIVE payment method once activated. """ payinMethods: [MemberPaymentMethod!]! """ - List of active and pending payment payout methods for this member. + List of all member's ACTIVE and PENDING payment payout methods. + A member can have multiple ACTIVE payment methods with these constraints: + - Only one ACTIVE payment method per provider, eg. one Trustly, one Swish, one Nordea etc. + - Only one ACTIVE payment method can be default at a time. + A member can have multiple PENDING payment methods with these constraints: + - Only one PENDING payment method per provider, eg. one Trustly, one Swish, one Nordea etc. + So there can exist max two payment methods per provider, one ACTIVE and one PENDING. + If a PENDING payment method has isDefault=true, then it will become the default ACTIVE payment method once activated. """ payoutMethods: [MemberPaymentMethod!]! """ - The default payment method for payin if any. + The default payment method to use for payins if any. + Note that there can exist a PENDING payment method in `payinMethods` list with `isDefault`=true, in that case this default + payment method will be replaced by it once the pending method is activated. """ defaultPayinMethod: MemberPaymentMethod """ - The default payment method for payout if any. + The default payment method to use for payouts if any. + Note that there can exist a PENDING payment method in `payoutMethods` list with `isDefault`=true, in that case this default + payment method will be replaced by it once the pending method is activated. """ defaultPayoutMethod: MemberPaymentMethod """ - The available payment methods that the member can choose from when setting up a new payment method. + The available payment methods that the member can choose from when setting up a new payment method. + This list can include both payment methods that the member has already set up and new payment methods that the + member has not yet set up but are available to them. For already set up payment methods, the `isActive` field will + be true and the `details` field will be populated with the payment method details. For new payment methods that the + member has not yet set up, the `isActive` field will be false and the `details` field will be null. + If member picks a new payment method to set up, the corresponding mutation for setting up that payment method should + be called, eg. `paymentMethodSetupTrustly`, `paymentMethodSetupSwishPayin` etc. + If member picks an already set up payment method to set up as default, then `paymentMethodSetDefaultPayin` or + `paymentMethodSetDefaultPayout` mutation should be called depending on if it's a payin or payout method. """ availableMethods: [MemberPaymentAvailablePaymentMethod!]! """ @@ -3392,9 +3483,9 @@ type Mutation { """ Setup invoice payment method for the member. Kivra will be used as the provider if supported, else mail. """ - paymentMethodSetupInvoicePayin(input: PaymentMethodSetupInvoicePayinInput!): PaymentMethodSetupOutput! + paymentMethodSetupInvoicePayin: PaymentMethodSetupOutput! """ - Setup Trustly payment payin and payout method for the member. + Setup Trustly payment payin and payout method for the member. Requires member consent via redirect to Trustly URL in response. """ paymentMethodSetupTrustly(input: PaymentMethodSetupTrustlyInput!): PaymentMethodSetupOutput! """ @@ -3406,17 +3497,19 @@ type Mutation { """ paymentMethodSetupSwishPayout(input: PaymentMethodSetupSwishInput!): PaymentMethodSetupOutput! """ - Setup Swish payin method for the member. + Setup Swish payin method for the member. Requires member consent in Swish app. """ paymentMethodSetupSwishPayin(input: PaymentMethodSetupSwishInput!): PaymentMethodSetupOutput! """ - Revoke an active payment method. The member will be required to set up a new payment method if they revoke their default one. + A member can have multiple ACTIVE payment methods where one of those is default. This mutation changes the + members default payment method for charging to any of his/hers other active payment methods. """ - paymentMethodRevoke(id: ID!): UserError + paymentMethodSetDefaultPayin(provider: MemberPaymentProvider!): UserError """ - Set an active payment method as default. + A member can have multiple ACTIVE payment methods where one of those is default. This mutation changes the + members default payment method for payouts to any of his/hers other active payment methods. """ - paymentMethodSetDefault(id: ID!): UserError + paymentMethodSetDefaultPayout(provider: MemberPaymentProvider!): UserError """ Start a conversation. This is effectively creating one, but with two slight differences from a regular "create something"-mutation: @@ -3545,7 +3638,7 @@ type Mutation { Update the raw insurance-related data for this `PriceIntent`. This data is mostly related to the insured object itself, and not the "holder" of the insurance. """ - priceIntentDataUpdate(priceIntentId: UUID!, data: PricingFormData!): PriceIntentMutationOutput! + priceIntentDataUpdate(priceIntentId: UUID!, data: PricingFormData!, applySuggestedData: Boolean): PriceIntentMutationOutput! """ Associate a specific Insurely `dataCollectionId` from lookup-service with this PriceIntent. """ @@ -3576,6 +3669,10 @@ type Mutation { """ productOfferReprice(offerId: UUID!, data: PricingFormData!): ProductOffersMutationOutput! """ + Mark a young pet guide story as read for a specific member. + """ + puppyGuideEngagement(engagement: PuppyEngagementInput!): PuppyGuideStoryMutationOutput! + """ Update the customer of the shop session. Only non-null fields will be changed. Can trigger automatic lookup of other information. The session can be placed in a "point of no return" state where it is no longer legal to update the customer, @@ -3676,6 +3773,22 @@ type Mutation { """ upsellTravelAddonActivate(quoteId: ID!, addonId: ID!): UpsellTravelAddonActivationOutput! } +type PartnerClaim { + id: ID! + externalId: String! + exposureDisplayName: String + status: ClaimStatus + submittedAt: Date + payoutAmount: Money + associatedTypeOfContract: String + claimType: String + handlerEmail: String + displayItems: [ClaimDisplayItem!]! + """ + Terms & conditions for the claim found using claims contractId and dateOfOccurrence, otherwise null. + """ + productVariant: ProductVariant +} type PartnerData { sas: SasPartnerData } @@ -3707,17 +3820,7 @@ type PaymentMethodInvoiceDetails { """ email: String } -input PaymentMethodSetupInvoicePayinInput { - """ - Set up invoice payment method as default. - """ - setAsDefaultPayout: Boolean! -} input PaymentMethodSetupNordeaPayoutInput { - """ - Set up Nordea payout method as default. - """ - setAsDefault: Boolean! """ The clearing number for member's bank account. """ @@ -3732,6 +3835,10 @@ type PaymentMethodSetupOutput { The status of the setup process. If FAILED the reason for failure can be found in the `error` field. """ status: PaymentMethodSetupStatus! + """ + The order id for the payment method setup order if SUCCESSFUL. + """ + orderId: ID """ Url to redirect the member to if any. """ @@ -3756,24 +3863,12 @@ enum PaymentMethodSetupStatus { FAILED } input PaymentMethodSetupSwishInput { - """ - Set up Swish payment method as default. - """ - setAsDefault: Boolean! """ The Swish mobile number to use for payout or payin. """ phoneNumber: String! } input PaymentMethodSetupTrustlyInput { - """ - Set up Trustly payment method as default for payin. - """ - setAsDefaultPayin: Boolean! - """ - Set up Trustly payment method as default for payout. - """ - setAsDefaultPayout: Boolean! """ The URL to redirect the member back to after a successful setup after Trustly onboarding. """ @@ -3873,11 +3968,11 @@ type PriceIntent { """ product: Product! """ - Submitted user form data. + UI-safe masked form data. PII fields (street, zipCode, city) are always masked. """ data: PricingFormData! """ - Data submitted in other places or inferred from other data points + Masked, uncommitted form data from automatic lookup (e.g. SPAR address, trial data). PII fields are masked the same way as 'data'. """ suggestedData: PricingFormData! """ @@ -3905,6 +4000,15 @@ type PriceIntent { When 'true' it means user has gone trough Insurely flow with that price intent """ hasCollectedInsurelyData: Boolean! + """ + List of external insurances fetched via Insurely that correspond to products Hedvig offers. + Null when no Insurely data collection has been associated with this price intent. + """ + fetchedExternalInsurances: [FetchedExternalInsurance!] + """ + When 'true' all required form data has been provided and the price intent can be confirmed. + """ + isReadyToConfirm: Boolean! } enum PriceIntentAnimal { CAT @@ -4065,7 +4169,7 @@ type ProductOffer { """ priceIntentId: UUID """ - The form data used to generate the offer + UI-safe masked form data used to generate the offer. PII fields (street, zipCode, city) are masked when address came from registration address lookup. """ priceIntentData: PricingFormData! """ @@ -4299,6 +4403,53 @@ type ProductVariantComparisonRow { """ covered: [String!]! } +input PuppyEngagementInput { + name: String! + rating: Int + opened: Boolean + read: Boolean + closed: Boolean +} +type PuppyGuideStory { + """ + The unique name/identifier of the story. + """ + name: String! + """ + The display title of the story. + """ + title: String! + """ + The subtitle or description of the story. + """ + subtitle: String! + """ + The main content of the story. + """ + content: String! + """ + The image associated with this story. + """ + image: String! + """ + Categories this story belongs to. + """ + categories: [String!]! + """ + The date when the story was marked as read by the user. + """ + read: Boolean! + """ + The user's rating of the story. + """ + rating: Int +} +type PuppyGuideStoryMutationOutput { + """ + Indicates whether the mutation was successful. + """ + success: Boolean! +} type Query { """ Return a conversation for a given ID. @@ -4306,6 +4457,7 @@ type Query { """ conversation(id: UUID!): Conversation claim(id: ID!): Claim + partnerClaim(id: ID!): PartnerClaim claimIntent(id: ID!): ClaimIntent! claimIntentFormFieldSearch(input: ClaimIntentFormFieldSearchInput!): ClaimIntentFormFieldSearchOutput! personalInformation(input: PersonalInformationInput!): PersonalInformation @@ -4614,6 +4766,12 @@ type ShopSessionOutcome { Note that this will not contain and `PendingContract`s. """ createdContracts: [Contract!]! + """ + Pre-hashed Facebook CAPI user data (SHA-256 hex, lowercase) for this signed session. + Fields follow Meta's Conversions API customer information parameter spec. + Requires authentication (inherits MemberAccessible from the outcome). + """ + capiUserData: CapiUserData! } type ShopSessionSigning { id: UUID! diff --git a/app/app/build.gradle.kts b/app/app/build.gradle.kts index a1029b89c3..53719f71d6 100644 --- a/app/app/build.gradle.kts +++ b/app/app/build.gradle.kts @@ -190,7 +190,6 @@ dependencies { implementation(projects.featureClaimChat) implementation(projects.featureClaimDetails) implementation(projects.featureClaimHistory) - implementation(projects.featureConnectPaymentTrustly) implementation(projects.featureCrossSellSheet) implementation(projects.featureDeleteAccount) diff --git a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt index d1c6cc480c..3a1292c054 100644 --- a/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt +++ b/app/app/src/main/kotlin/com/hedvig/android/app/navigation/HedvigNavHost.kt @@ -180,8 +180,8 @@ internal fun HedvigNavHost( onNavigateToNewConversation = { navigateToNewConversation() }, - navigateToClaimDetails = { claimId -> - navController.navigate(ClaimDetailDestination.ClaimOverviewDestination(claimId)) + navigateToClaimDetails = { claimId, isPartnerClaim -> + navController.navigate(ClaimDetailDestination.ClaimOverviewDestination(claimId, isPartnerClaim)) }, navigateToConnectPayment = navigateToConnectPayment, navigateToMissingInfo = { contractId: String, type: CoInsuredFlowType -> @@ -351,8 +351,8 @@ internal fun HedvigNavHost( nestedGraphs = { claimHistoryGraph( navigateUp = navController::navigateUp, - navigateToClaimDetails = { claimId -> - navController.navigate(ClaimDetailDestination.ClaimOverviewDestination(claimId)) + navigateToClaimDetails = { claimId, isPartnerClaim -> + navController.navigate(ClaimDetailDestination.ClaimOverviewDestination(claimId, isPartnerClaim)) }, ) }, @@ -383,7 +383,7 @@ internal fun HedvigNavHost( navigateToChipId = { navController.navigate(ChipIdGraphDestination()) }, - languageService = languageService + languageService = languageService, ) cbmChatGraph( hedvigDeepLinkContainer = hedvigDeepLinkContainer, @@ -422,10 +422,10 @@ internal fun HedvigNavHost( hedvigDeepLinkContainer = hedvigDeepLinkContainer, popBackStackOrFinish = popBackStackOrFinish, goHome = { - navController.navigate(HomeDestination.Graph) { - popUpTo(ChipIdGraphDestination::class) { inclusive = true } - } - } + navController.navigate(HomeDestination.Graph) { + popUpTo(ChipIdGraphDestination::class) { inclusive = true } + } + }, ) movingFlowGraph( navController = navController, diff --git a/app/feature/feature-claim-details/src/main/graphql/QueryPartnerClaim.graphql b/app/feature/feature-claim-details/src/main/graphql/QueryPartnerClaim.graphql new file mode 100644 index 0000000000..d6da1d153a --- /dev/null +++ b/app/feature/feature-claim-details/src/main/graphql/QueryPartnerClaim.graphql @@ -0,0 +1,5 @@ +query PartnerClaimDetail($claimId: ID!) { + partnerClaim(id: $claimId) { + ...PartnerClaimFragment + } +} diff --git a/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/data/GetClaimDetailUiStateUseCase.kt b/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/data/GetClaimDetailUiStateUseCase.kt index c24506a857..e6af1602ae 100644 --- a/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/data/GetClaimDetailUiStateUseCase.kt +++ b/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/data/GetClaimDetailUiStateUseCase.kt @@ -13,6 +13,7 @@ import com.hedvig.android.data.display.items.DisplayItem import com.hedvig.android.feature.claim.details.ui.ClaimDetailUiState import com.hedvig.android.ui.claimstatus.model.ClaimStatusCardUiState import com.hedvig.audio.player.data.SignedAudioUrl +import kotlin.time.Clock import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.delay @@ -22,9 +23,12 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.isActive import kotlinx.datetime.TimeZone +import kotlinx.datetime.atStartOfDayIn import kotlinx.datetime.toLocalDateTime import octopus.ClaimQuery +import octopus.PartnerClaimDetailQuery import octopus.fragment.ClaimFragment +import octopus.fragment.PartnerClaimFragment import octopus.type.ClaimOutcome import octopus.type.ClaimStatus import octopus.type.InsuranceDocumentType @@ -33,11 +37,14 @@ internal class GetClaimDetailUiStateUseCase( private val apolloClient: ApolloClient, private val crossSellAfterClaimClosedRepository: CrossSellAfterClaimClosedRepository, ) { - fun invoke(claimId: String): Flow> { + fun invoke(claimId: String, isPartnerClaim: Boolean = false): Flow> { return flow { while (currentCoroutineContext().isActive) { - val queryFlow = queryFlow(claimId) - emitAll(queryFlow) + if (isPartnerClaim) { + emitAll(partnerQueryFlow(claimId)) + } else { + emitAll(queryFlow(claimId)) + } delay(POLL_INTERVAL) } } @@ -60,6 +67,62 @@ internal class GetClaimDetailUiStateUseCase( } } + private fun partnerQueryFlow(claimId: String): Flow> { + return apolloClient + .query(PartnerClaimDetailQuery(claimId)) + .fetchPolicy(FetchPolicy.NetworkOnly) + .safeFlow { Error.NetworkError } + .map { response -> + either { + val claim = response.bind().partnerClaim + ensureNotNull(claim) { Error.NoClaimFound } + fromPartnerClaim(claim) + } + } + } + + private fun fromPartnerClaim(claim: PartnerClaimFragment): ClaimDetailUiState.Content { + val termsConditionsUrl = claim.productVariant?.documents + ?.firstOrNull { it.type == InsuranceDocumentType.TERMS_AND_CONDITIONS }?.url + val submittedAt = claim.submittedAt + ?.atStartOfDayIn(TimeZone.UTC) + ?.toLocalDateTime(TimeZone.UTC) + ?: Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()) + + return ClaimDetailUiState.Content( + claimId = claim.id, + conversationId = null, + hasUnreadMessages = false, + submittedContent = null, + files = emptyList(), + claimStatusCardUiState = ClaimStatusCardUiState.fromPartnerClaim(claim), + claimStatus = when (claim.status) { + ClaimStatus.CREATED -> ClaimDetailUiState.Content.ClaimStatus.CREATED + ClaimStatus.IN_PROGRESS -> ClaimDetailUiState.Content.ClaimStatus.IN_PROGRESS + ClaimStatus.CLOSED -> ClaimDetailUiState.Content.ClaimStatus.CLOSED + ClaimStatus.REOPENED -> ClaimDetailUiState.Content.ClaimStatus.REOPENED + ClaimStatus.UNKNOWN__, null -> ClaimDetailUiState.Content.ClaimStatus.UNKNOWN + }, + claimOutcome = ClaimDetailUiState.Content.ClaimOutcome.UNKNOWN, + uploadUri = "", + isUploadingFile = false, + uploadError = null, + claimType = claim.claimType, + insuranceDisplayName = claim.exposureDisplayName ?: claim.productVariant?.displayName, + submittedAt = submittedAt, + termsConditionsUrl = termsConditionsUrl, + savedFileUri = null, + downloadError = null, + isLoadingPdf = null, + appealInstructionsUrl = null, + isUploadingFilesEnabled = false, + infoText = null, + displayItems = claim.displayItems.map { + DisplayItem.fromStrings(it.displayTitle, it.displayValue) + }, + ) + } + private fun ClaimDetailUiState.Content.Companion.fromClaim( claim: ClaimFragment, conversationId: String?, diff --git a/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/di/FeatureClaimDetailsModule.kt b/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/di/FeatureClaimDetailsModule.kt index 5712938434..ed9104b752 100644 --- a/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/di/FeatureClaimDetailsModule.kt +++ b/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/di/FeatureClaimDetailsModule.kt @@ -23,9 +23,10 @@ val claimDetailsModule = module { initialFilesUri = initialFilesUri, ) } - viewModel { (claimId: String) -> + viewModel { (claimId: String, isPartnerClaim: Boolean) -> ClaimDetailsViewModel( claimId = claimId, + isPartnerClaim = isPartnerClaim, getClaimDetailUiStateUseCase = get(), claimsServiceUploadFileUseCase = get(), downloadPdfUseCase = get(), diff --git a/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/navigation/ClaimDetailDestinationGraph.kt b/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/navigation/ClaimDetailDestinationGraph.kt index 09ed0be3a1..814d8abd40 100644 --- a/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/navigation/ClaimDetailDestinationGraph.kt +++ b/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/navigation/ClaimDetailDestinationGraph.kt @@ -31,7 +31,7 @@ fun NavGraphBuilder.claimDetailsGraph( navdestination( deepLinks = navDeepLinks(hedvigDeepLinkContainer.claimDetails), ) { - val viewModel: ClaimDetailsViewModel = koinViewModel { parametersOf(claimId) } + val viewModel: ClaimDetailsViewModel = koinViewModel { parametersOf(claimId, isPartnerClaim) } val context = LocalContext.current ClaimDetailsDestination( viewModel = viewModel, diff --git a/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/navigation/ClaimDetailDestinations.kt b/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/navigation/ClaimDetailDestinations.kt index fb294faa5d..4404e5a59e 100644 --- a/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/navigation/ClaimDetailDestinations.kt +++ b/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/navigation/ClaimDetailDestinations.kt @@ -12,6 +12,7 @@ sealed interface ClaimDetailDestination { */ @SerialName("claimId") val claimId: String, + val isPartnerClaim: Boolean = false, ) : ClaimDetailDestination, Destination } diff --git a/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/ui/ClaimDetailsDestination.kt b/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/ui/ClaimDetailsDestination.kt index 7ab7f80e6a..7b7199c1a9 100644 --- a/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/ui/ClaimDetailsDestination.kt +++ b/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/ui/ClaimDetailsDestination.kt @@ -380,12 +380,11 @@ private fun NonDynamicGrid( } @Composable -internal fun ExplanationBottomSheet(sheetState: HedvigBottomSheetState) { +private fun ExplanationBottomSheet(sheetState: HedvigBottomSheetState) { HedvigBottomSheet(sheetState) { _ -> HedvigText( text = stringResource(Res.string.claim_status_claim_details_info_text), - modifier = Modifier - .fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) Spacer(Modifier.height(32.dp)) HedvigTextButton( @@ -504,29 +503,31 @@ private fun BeforeGridContent( .fillMaxWidth() .padding(horizontal = 2.dp), ) - Spacer(Modifier.height(24.dp)) - HedvigText( - stringResource(Res.string.claim_status_detail_uploaded_files_info_title), - Modifier.padding(horizontal = 2.dp), - ) - Spacer(Modifier.height(8.dp)) - when (uiState.submittedContent) { - is ClaimDetailUiState.Content.SubmittedContent.Audio -> { - ClaimDetailHedvigAudioPlayerItem(uiState.submittedContent.signedAudioURL) - } + if (uiState.submittedContent != null || uiState.files.isNotEmpty()) { + Spacer(Modifier.height(24.dp)) + HedvigText( + stringResource(Res.string.claim_status_detail_uploaded_files_info_title), + Modifier.padding(horizontal = 2.dp), + ) + Spacer(Modifier.height(8.dp)) + when (uiState.submittedContent) { + is ClaimDetailUiState.Content.SubmittedContent.Audio -> { + ClaimDetailHedvigAudioPlayerItem(uiState.submittedContent.signedAudioURL) + } - is ClaimDetailUiState.Content.SubmittedContent.FreeText -> { - HedvigCard(Modifier.fillMaxWidth()) { - HedvigText( - uiState.submittedContent.text, - Modifier.padding(16.dp), - ) + is ClaimDetailUiState.Content.SubmittedContent.FreeText -> { + HedvigCard(Modifier.fillMaxWidth()) { + HedvigText( + uiState.submittedContent.text, + Modifier.padding(16.dp), + ) + } } - } - else -> {} + else -> {} + } + Spacer(Modifier.height(8.dp)) } - Spacer(Modifier.height(8.dp)) } @Composable diff --git a/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/ui/ClaimDetailsViewModel.kt b/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/ui/ClaimDetailsViewModel.kt index 4276b6be30..68ca781376 100644 --- a/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/ui/ClaimDetailsViewModel.kt +++ b/app/feature/feature-claim-details/src/main/kotlin/com/hedvig/android/feature/claim/details/ui/ClaimDetailsViewModel.kt @@ -32,16 +32,24 @@ import kotlinx.datetime.LocalDateTime internal class ClaimDetailsViewModel( claimId: String, + isPartnerClaim: Boolean, getClaimDetailUiStateUseCase: GetClaimDetailUiStateUseCase, claimsServiceUploadFileUseCase: ClaimsServiceUploadFileUseCase, downloadPdfUseCase: DownloadPdfUseCase, ) : MoleculeViewModel( ClaimDetailUiState.Loading, - ClaimDetailPresenter(claimId, getClaimDetailUiStateUseCase, claimsServiceUploadFileUseCase, downloadPdfUseCase), + ClaimDetailPresenter( + claimId, + isPartnerClaim, + getClaimDetailUiStateUseCase, + claimsServiceUploadFileUseCase, + downloadPdfUseCase, + ), ) private class ClaimDetailPresenter( private val claimId: String, + private val isPartnerClaim: Boolean, private val getClaimDetailUiStateUseCase: GetClaimDetailUiStateUseCase, private val claimsServiceUploadFileUseCase: ClaimsServiceUploadFileUseCase, private val downloadPdfUseCase: DownloadPdfUseCase, @@ -60,7 +68,7 @@ private class ClaimDetailPresenter( LaunchedEffect(loadIteration) { isLoading = true hasError = false - getClaimDetailUiStateUseCase.invoke(claimId).collect { result -> + getClaimDetailUiStateUseCase.invoke(claimId, isPartnerClaim).collect { result -> isLoading = false result.fold( ifLeft = { diff --git a/app/feature/feature-claim-history/src/androidMain/kotlin/com/hedvig/android/feature/claimhistory/ClaimHistoryDestination.kt b/app/feature/feature-claim-history/src/androidMain/kotlin/com/hedvig/android/feature/claimhistory/ClaimHistoryDestination.kt index a6d1ea9a4a..70feccdbd4 100644 --- a/app/feature/feature-claim-history/src/androidMain/kotlin/com/hedvig/android/feature/claimhistory/ClaimHistoryDestination.kt +++ b/app/feature/feature-claim-history/src/androidMain/kotlin/com/hedvig/android/feature/claimhistory/ClaimHistoryDestination.kt @@ -59,7 +59,7 @@ import org.jetbrains.compose.resources.stringResource internal fun ClaimHistoryDestination( claimHistoryViewModel: ClaimHistoryViewModel, navigateUp: () -> Unit, - navigateToClaimDetails: (String) -> Unit, + navigateToClaimDetails: (String, Boolean) -> Unit, ) { val uiState by claimHistoryViewModel.uiState.collectAsStateWithLifecycle() ClaimHistoryScreen( @@ -74,7 +74,7 @@ internal fun ClaimHistoryDestination( private fun ClaimHistoryScreen( uiState: ClaimHistoryUiState, navigateUp: () -> Unit, - navigateToClaimDetails: (String) -> Unit, + navigateToClaimDetails: (String, Boolean) -> Unit, reload: () -> Unit, ) { HedvigScaffold( @@ -108,7 +108,10 @@ private fun ClaimHistoryScreen( .fillMaxWidth(), ) - is ClaimHistoryUiState.Content -> ClaimHistoryContent(uiState, navigateToClaimDetails) + is ClaimHistoryUiState.Content -> ClaimHistoryContent( + uiState, + navigateToClaimDetails, + ) } } } @@ -117,7 +120,7 @@ private fun ClaimHistoryScreen( @Composable private fun ColumnScope.ClaimHistoryContent( uiState: ClaimHistoryUiState.Content, - navigateToClaimDetails: (String) -> Unit, + navigateToClaimDetails: (String, Boolean) -> Unit, ) { uiState.claims.forEachIndexed { index, claim -> ClaimHistoryItem(index, claim, navigateToClaimDetails) @@ -125,7 +128,7 @@ private fun ColumnScope.ClaimHistoryContent( } @Composable -private fun ClaimHistoryItem(index: Int, claim: ClaimHistory, navigateToClaimDetails: (String) -> Unit) { +private fun ClaimHistoryItem(index: Int, claim: ClaimHistory, navigateToClaimDetails: (String, Boolean) -> Unit) { val hedvigDateTimeFormatter = rememberHedvigDateTimeFormatter() HorizontalItemsWithMaximumSpaceTaken( { @@ -179,7 +182,7 @@ private fun ClaimHistoryItem(index: Int, claim: ClaimHistory, navigateToClaimDet .fillMaxWidth() .clickable( onClick = dropUnlessResumed { - navigateToClaimDetails(claim.id) + navigateToClaimDetails(claim.id, claim.isPartnerClaim) }, ) .horizontalDivider(DividerPosition.Top, show = index != 0, horizontalPadding = 18.dp) @@ -197,7 +200,7 @@ private fun PreviewClaimHistoryScreen( ClaimHistoryScreen( uiState = uiState, {}, - {}, + { _, _ -> }, {}, ) } @@ -214,6 +217,7 @@ private class ClaimHistoryUiStateCollectionPreviewParameterProvider : claimType = "$it", outcome = ClaimHistory.ClaimOutcome.entries[it], submittedAt = Instant.fromEpochMilliseconds(100), + isPartnerClaim = false, ) }.toNonEmptyListOrThrow(), ), diff --git a/app/feature/feature-claim-history/src/androidMain/kotlin/com/hedvig/android/feature/claimhistory/nav/ClaimHistoryDestination.kt b/app/feature/feature-claim-history/src/androidMain/kotlin/com/hedvig/android/feature/claimhistory/nav/ClaimHistoryDestination.kt index 2011549621..2bcecbd7b5 100644 --- a/app/feature/feature-claim-history/src/androidMain/kotlin/com/hedvig/android/feature/claimhistory/nav/ClaimHistoryDestination.kt +++ b/app/feature/feature-claim-history/src/androidMain/kotlin/com/hedvig/android/feature/claimhistory/nav/ClaimHistoryDestination.kt @@ -11,7 +11,10 @@ import org.koin.compose.viewmodel.koinViewModel @Serializable data object ClaimHistoryDestination : Destination -fun NavGraphBuilder.claimHistoryGraph(navigateUp: () -> Unit, navigateToClaimDetails: (String) -> Unit) { +fun NavGraphBuilder.claimHistoryGraph( + navigateUp: () -> Unit, + navigateToClaimDetails: (claimId: String, isPartnerClaim: Boolean) -> Unit, +) { navdestination { ClaimHistoryDestination( claimHistoryViewModel = koinViewModel(), diff --git a/app/feature/feature-claim-history/src/commonMain/graphql/QueryClaimsHistory.graphql b/app/feature/feature-claim-history/src/commonMain/graphql/QueryClaimsHistory.graphql index 2d24c3c392..70835ca9f8 100644 --- a/app/feature/feature-claim-history/src/commonMain/graphql/QueryClaimsHistory.graphql +++ b/app/feature/feature-claim-history/src/commonMain/graphql/QueryClaimsHistory.graphql @@ -3,5 +3,8 @@ query ClaimsHistory { claimsHistory { ...ClaimFragment } + partnerClaimsHistory { + ...PartnerClaimFragment + } } } diff --git a/app/feature/feature-claim-history/src/commonMain/kotlin/com/hedvig/android/feature/claimhistory/GetClaimsHistoryUseCase.kt b/app/feature/feature-claim-history/src/commonMain/kotlin/com/hedvig/android/feature/claimhistory/GetClaimsHistoryUseCase.kt index d6cda3be07..0aa2bf3942 100644 --- a/app/feature/feature-claim-history/src/commonMain/kotlin/com/hedvig/android/feature/claimhistory/GetClaimsHistoryUseCase.kt +++ b/app/feature/feature-claim-history/src/commonMain/kotlin/com/hedvig/android/feature/claimhistory/GetClaimsHistoryUseCase.kt @@ -6,9 +6,12 @@ import com.apollographql.apollo.ApolloClient import com.hedvig.android.apollo.ErrorMessage import com.hedvig.android.apollo.safeFlow import com.hedvig.android.core.common.ErrorMessage +import kotlin.time.Clock import kotlin.time.Instant import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import kotlinx.datetime.TimeZone +import kotlinx.datetime.atStartOfDayIn import octopus.ClaimsHistoryQuery import octopus.type.ClaimOutcome @@ -21,30 +24,32 @@ internal class GetClaimsHistoryUseCase( .safeFlow(::ErrorMessage) .map { result -> either { - result - .bind() - .currentMember - .claimsHistory - .map { history -> - ClaimHistory( - id = history.id, - claimType = history.claimType, - submittedAt = history.submittedAt, - outcome = when (history.outcome) { - ClaimOutcome.PAID -> ClaimHistory.ClaimOutcome.PAID - - ClaimOutcome.NOT_COMPENSATED -> ClaimHistory.ClaimOutcome.NOT_COMPENSATED - - ClaimOutcome.NOT_COVERED -> ClaimHistory.ClaimOutcome.NOT_COVERED - - ClaimOutcome.UNRESPONSIVE -> ClaimHistory.ClaimOutcome.UNRESPONSIVE - - ClaimOutcome.UNKNOWN__, - null, - -> ClaimHistory.ClaimOutcome.UNKNOWN - }, - ) - } + val data = result.bind() + val regularClaims = data.currentMember.claimsHistory.map { history -> + ClaimHistory( + id = history.id, + claimType = history.claimType, + submittedAt = history.submittedAt, + outcome = when (history.outcome) { + ClaimOutcome.PAID -> ClaimHistory.ClaimOutcome.PAID + ClaimOutcome.NOT_COMPENSATED -> ClaimHistory.ClaimOutcome.NOT_COMPENSATED + ClaimOutcome.NOT_COVERED -> ClaimHistory.ClaimOutcome.NOT_COVERED + ClaimOutcome.UNRESPONSIVE -> ClaimHistory.ClaimOutcome.UNRESPONSIVE + ClaimOutcome.UNKNOWN__, null -> ClaimHistory.ClaimOutcome.UNKNOWN + }, + isPartnerClaim = false, + ) + } + val partnerClaims = data.currentMember.partnerClaimsHistory.map { history -> + ClaimHistory( + id = history.id, + claimType = history.claimType, + submittedAt = history.submittedAt?.atStartOfDayIn(TimeZone.UTC) ?: Clock.System.now(), + outcome = ClaimHistory.ClaimOutcome.UNKNOWN, + isPartnerClaim = true, + ) + } + (regularClaims + partnerClaims).sortedByDescending { it.submittedAt } } } } @@ -57,6 +62,7 @@ internal data class ClaimHistory( // Subtitle uses this date to show when the claim was submitted val submittedAt: Instant, val outcome: ClaimOutcome?, + val isPartnerClaim: Boolean, ) { enum class ClaimOutcome { PAID, diff --git a/app/feature/feature-home/src/main/graphql/QueryHome.graphql b/app/feature/feature-home/src/main/graphql/QueryHome.graphql index 448965eb09..c333001488 100644 --- a/app/feature/feature-home/src/main/graphql/QueryHome.graphql +++ b/app/feature/feature-home/src/main/graphql/QueryHome.graphql @@ -6,6 +6,9 @@ query Home($claimsHistoryFlag: Boolean!) { claimsActive@include(if: $claimsHistoryFlag) { ...ClaimFragment } + partnerClaimsActive { + ...PartnerClaimFragment + } terminatedContracts { id currentAgreement { diff --git a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/data/GetHomeDataUseCase.kt b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/data/GetHomeDataUseCase.kt index 182b2ff26a..b6a910de89 100644 --- a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/data/GetHomeDataUseCase.kt +++ b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/data/GetHomeDataUseCase.kt @@ -45,7 +45,6 @@ import kotlinx.datetime.TimeZone import kotlinx.datetime.atStartOfDayIn import octopus.HomeQuery import octopus.UnreadMessageCountQuery -import octopus.fragment.ClaimFragment import octopus.fragment.HomeCrossSellFragment internal interface GetHomeDataUseCase { @@ -268,11 +267,20 @@ internal class GetHomeDataUseCaseImpl( } private fun HomeQuery.Data.claimStatusCards(): HomeData.ClaimStatusCardsData? { - val claimStatusCards: NonEmptyList = - this.currentMember.claims?.toNonEmptyListOrNull() - ?: this.currentMember.claimsActive?.toNonEmptyListOrNull() - ?: return null - return HomeData.ClaimStatusCardsData(claimStatusCards.map(ClaimStatusCardUiState::fromClaimStatusCardsQuery)) + val regularCards = + this.currentMember.claims.orEmpty().map(ClaimStatusCardUiState::fromClaimStatusCardsQuery) + + this.currentMember.claimsActive.orEmpty().map(ClaimStatusCardUiState::fromClaimStatusCardsQuery) + val partnerClaims = this.currentMember.partnerClaimsActive + val partnerCards = partnerClaims.map(ClaimStatusCardUiState::fromPartnerClaim) + + val allCards = (regularCards + partnerCards) + .sortedByDescending { it.submittedDate } + .toNonEmptyListOrNull() ?: return null + + return HomeData.ClaimStatusCardsData( + claimStatusCardsUiState = allCards, + partnerClaimIds = partnerClaims.map { it.id }.toSet(), + ) } internal data class HomeData( @@ -290,6 +298,7 @@ internal data class HomeData( @Immutable data class ClaimStatusCardsData( val claimStatusCardsUiState: NonEmptyList, + val partnerClaimIds: Set = emptySet(), ) data class VeryImportantMessage( diff --git a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/navigation/HomeGraph.kt b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/navigation/HomeGraph.kt index 072b01417f..9ef6db5136 100644 --- a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/navigation/HomeGraph.kt +++ b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/navigation/HomeGraph.kt @@ -22,7 +22,7 @@ fun NavGraphBuilder.homeGraph( navController: NavController, onNavigateToInbox: () -> Unit, onNavigateToNewConversation: () -> Unit, - navigateToClaimDetails: (claimId: String) -> Unit, + navigateToClaimDetails: (claimId: String, isPartnerClaim: Boolean) -> Unit, navigateToConnectPayment: () -> Unit, navigateToContactInfo: () -> Unit, navigateToMissingInfo: (String, CoInsuredFlowType) -> Unit, @@ -50,8 +50,8 @@ fun NavGraphBuilder.homeGraph( onNavigateToNewConversation = dropUnlessResumed { onNavigateToNewConversation() }, navigateToClaimChat = dropUnlessResumed { navigateToClaimChat() }, navigateToClaimChatInDevMode = dropUnlessResumed { navigateToClaimChatInDevMode() }, - onClaimDetailCardClicked = dropUnlessResumed { claimId: String -> - navigateToClaimDetails(claimId) + onClaimDetailCardClicked = dropUnlessResumed { claimId: String, isPartnerClaim: Boolean -> + navigateToClaimDetails(claimId, isPartnerClaim) }, navigateToConnectPayment = dropUnlessResumed { navigateToConnectPayment() }, navigateToMissingInfo = dropUnlessResumed { contractId, type -> navigateToMissingInfo(contractId, type) }, diff --git a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/ui/HomeDestination.kt b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/ui/HomeDestination.kt index 71a9ae2965..cb54a0b14e 100644 --- a/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/ui/HomeDestination.kt +++ b/app/feature/feature-home/src/main/kotlin/com/hedvig/android/feature/home/home/ui/HomeDestination.kt @@ -156,7 +156,7 @@ internal fun HomeDestination( onNavigateToNewConversation: () -> Unit, navigateToClaimChat: () -> Unit, navigateToClaimChatInDevMode: () -> Unit, - onClaimDetailCardClicked: (String) -> Unit, + onClaimDetailCardClicked: (claimId: String, isPartnerClaim: Boolean) -> Unit, navigateToConnectPayment: () -> Unit, navigateToHelpCenter: () -> Unit, openUrl: (String) -> Unit, @@ -206,7 +206,7 @@ private fun HomeScreen( onNavigateToNewConversation: () -> Unit, navigateToClaimChat: () -> Unit, navigateToClaimChatInDevMode: () -> Unit, - onClaimDetailCardClicked: (String) -> Unit, + onClaimDetailCardClicked: (claimId: String, isPartnerClaim: Boolean) -> Unit, navigateToConnectPayment: () -> Unit, navigateToHelpCenter: () -> Unit, openUrl: (String) -> Unit, @@ -421,7 +421,7 @@ private fun HomeScreenSuccess( pullRefreshState: PullRefreshState, toolbarHeight: Dp, notificationPermissionState: NotificationPermissionState, - onClaimDetailCardClicked: (claimId: String) -> Unit, + onClaimDetailCardClicked: (claimId: String, isPartnerClaim: Boolean) -> Unit, navigateToConnectPayment: () -> Unit, navigateToHelpCenter: () -> Unit, openClaimFlowSheet: () -> Unit, @@ -474,7 +474,12 @@ private fun HomeScreenSuccess( claimStatusCards = { if (uiState.claimStatusCardsData != null) { ClaimStatusCards( - onClick = onClaimDetailCardClicked, + onClick = { claimId -> + onClaimDetailCardClicked( + claimId, + claimId in (uiState.claimStatusCardsData.partnerClaimIds), + ) + }, claimStatusCardsUiState = uiState.claimStatusCardsData.claimStatusCardsUiState, contentPadding = PaddingValues(horizontal = 16.dp) + horizontalInsets, ) @@ -799,7 +804,7 @@ private fun PreviewHomeScreen( onNavigateToInbox = {}, onNavigateToNewConversation = {}, navigateToClaimChat = {}, - onClaimDetailCardClicked = {}, + onClaimDetailCardClicked = { _, _ -> }, navigateToConnectPayment = {}, navigateToHelpCenter = {}, openUrl = {}, @@ -831,7 +836,7 @@ private fun PreviewHomeScreenWithError() { onNavigateToInbox = {}, onNavigateToNewConversation = {}, navigateToClaimChat = {}, - onClaimDetailCardClicked = {}, + onClaimDetailCardClicked = { _, _ -> }, navigateToConnectPayment = {}, navigateToHelpCenter = {}, openUrl = {}, @@ -884,7 +889,7 @@ private fun PreviewHomeScreenAllHomeTextTypes( onNavigateToInbox = {}, onNavigateToNewConversation = {}, navigateToClaimChat = {}, - onClaimDetailCardClicked = {}, + onClaimDetailCardClicked = { _, _ -> }, navigateToConnectPayment = {}, navigateToHelpCenter = {}, openUrl = {}, diff --git a/app/ui/claim-status/src/main/kotlin/com/hedvig/android/ui/claimstatus/model/ClaimPillType.kt b/app/ui/claim-status/src/main/kotlin/com/hedvig/android/ui/claimstatus/model/ClaimPillType.kt index abac84a1f8..4bea816734 100644 --- a/app/ui/claim-status/src/main/kotlin/com/hedvig/android/ui/claimstatus/model/ClaimPillType.kt +++ b/app/ui/claim-status/src/main/kotlin/com/hedvig/android/ui/claimstatus/model/ClaimPillType.kt @@ -25,6 +25,19 @@ sealed interface ClaimPillType { } companion object { + fun fromPartnerClaim(status: ClaimStatus?): List { + return when (status) { + ClaimStatus.CLOSED -> listOf(Closed.GenericClosed) + + ClaimStatus.CREATED, + ClaimStatus.IN_PROGRESS, + ClaimStatus.REOPENED, + ClaimStatus.UNKNOWN__, + null, + -> listOf(Claim) + } + } + fun fromClaimFragment(claim: ClaimFragment): List { return when (claim.status) { ClaimStatus.CREATED -> { diff --git a/app/ui/claim-status/src/main/kotlin/com/hedvig/android/ui/claimstatus/model/ClaimProgressSegment.kt b/app/ui/claim-status/src/main/kotlin/com/hedvig/android/ui/claimstatus/model/ClaimProgressSegment.kt index 9e31dee661..71dc5400f0 100644 --- a/app/ui/claim-status/src/main/kotlin/com/hedvig/android/ui/claimstatus/model/ClaimProgressSegment.kt +++ b/app/ui/claim-status/src/main/kotlin/com/hedvig/android/ui/claimstatus/model/ClaimProgressSegment.kt @@ -22,6 +22,19 @@ data class ClaimProgressSegment( } companion object { + fun fromPartnerClaim(status: ClaimStatus?): List = when (status) { + ClaimStatus.REOPENED, + ClaimStatus.IN_PROGRESS, + -> buildSegments(ACTIVE, ACTIVE, INACTIVE) + + ClaimStatus.CLOSED -> buildSegments(ACTIVE, ACTIVE, ACTIVE) + + ClaimStatus.CREATED, + ClaimStatus.UNKNOWN__, + null, + -> buildSegments(ACTIVE, INACTIVE, INACTIVE) + } + fun fromClaimFragment(claim: ClaimFragment): List = when (claim.status) { ClaimStatus.CREATED -> buildSegments(ACTIVE, INACTIVE, INACTIVE) diff --git a/app/ui/claim-status/src/main/kotlin/com/hedvig/android/ui/claimstatus/model/ClaimStatusCardUiState.kt b/app/ui/claim-status/src/main/kotlin/com/hedvig/android/ui/claimstatus/model/ClaimStatusCardUiState.kt index 823eccd16a..4163c0ebb6 100644 --- a/app/ui/claim-status/src/main/kotlin/com/hedvig/android/ui/claimstatus/model/ClaimStatusCardUiState.kt +++ b/app/ui/claim-status/src/main/kotlin/com/hedvig/android/ui/claimstatus/model/ClaimStatusCardUiState.kt @@ -1,7 +1,11 @@ package com.hedvig.android.ui.claimstatus.model +import kotlin.time.Clock import kotlin.time.Instant +import kotlinx.datetime.TimeZone +import kotlinx.datetime.atStartOfDayIn import octopus.fragment.ClaimFragment +import octopus.fragment.PartnerClaimFragment data class ClaimStatusCardUiState( val id: String, @@ -12,6 +16,17 @@ data class ClaimStatusCardUiState( val claimProgressItemsUiState: List, ) { companion object { + fun fromPartnerClaim(claim: PartnerClaimFragment): ClaimStatusCardUiState { + return ClaimStatusCardUiState( + id = claim.id, + claimType = claim.claimType, + insuranceDisplayName = null, + submittedDate = claim.submittedAt?.atStartOfDayIn(TimeZone.UTC) ?: Clock.System.now(), + pillTypes = ClaimPillType.fromPartnerClaim(claim.status), + claimProgressItemsUiState = ClaimProgressSegment.fromPartnerClaim(claim.status), + ) + } + fun fromClaimStatusCardsQuery(claim: ClaimFragment): ClaimStatusCardUiState { return ClaimStatusCardUiState( id = claim.id,