From 5cdbaf6694e4e8dd44ad956d857717db1454d3d6 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Tue, 30 Jun 2026 22:04:57 +0200 Subject: [PATCH 1/9] add tierDescription --- .../android/apollo/octopus/schema.graphqls | 114 +++++++++++++++++- .../MutationChangeTierDeductible.graphql | 2 + ...CreateChangeTierDeductibleIntentUseCase.kt | 4 +- .../stepcustomize/SelectCoverageViewModel.kt | 3 + .../ui/stepcustomize/SelectTierDestination.kt | 11 +- 5 files changed, 129 insertions(+), 5 deletions(-) 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 3e30d7a249..9b640dcbe3 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 @@ -442,6 +442,22 @@ type AgreementDisplayItem { displaySubtitle: String displayValue: String! } +input AnswersInput { + relationship: String + isPregnant: Boolean + hasKids: Boolean + hasCat: Boolean + hasDog: Boolean + yourAge: String + partnerAge: String + isStudent: Boolean + housingType: String + houseAge: String + isRental: Boolean + hasCar: Boolean + carAge: String + wantsAllRisk: Boolean +} enum AppPlatform { IOS ANDROID @@ -678,6 +694,10 @@ type ChangeTierDeductibleFromAgreement { The tier level """ tierLevel: Int + """ + Optional tier description, if set use that rather than productVariant->tierDescription + """ + tierDescription: String """ List of display items for the quote """ @@ -714,6 +734,10 @@ type ChangeTierDeductibleQuote { The tier level, a smaller number means lower coverage. Use this to sort the tier list. """ tierLevel: Int + """ + Optional tier description, if set use that rather than productVariant->tierDescription + """ + tierDescription: String """ The deductible if any """ @@ -872,6 +896,7 @@ type Claim { isUploadingFilesEnabled: Boolean! infoText: String displayItems: [ClaimDisplayItem!]! + contractId: String """ Terms & conditions for the claim found using claims contractId and dateOfOccurrence, otherwise null. """ @@ -1003,7 +1028,7 @@ type ClaimIntentStep { """ A union of all the different kinds of "step content". """ -union ClaimIntentStepContent = ClaimIntentStepContentForm|ClaimIntentStepContentSelect|ClaimIntentStepContentTask|ClaimIntentStepContentAudioRecording|ClaimIntentStepContentFileUpload|ClaimIntentStepContentSummary|ClaimIntentStepContentDeflection|ClaimIntentStepContentDeflectionMessage +union ClaimIntentStepContent = ClaimIntentStepContentForm|ClaimIntentStepContentSelect|ClaimIntentStepContentTask|ClaimIntentStepContentAudioRecording|ClaimIntentStepContentFileUpload|ClaimIntentStepContentSummary|ClaimIntentStepContentDeflection|ClaimIntentStepContentDeflectionMessage|ClaimIntentStepContentInformation """ An audio recording step is one where the user is meant to record some audio. Submitted using `Mutation.claimIntentSubmitAudio`. @@ -1181,6 +1206,22 @@ This typically will be backed by a String - but other formats could appear. """ scalar ClaimIntentStepContentFormFieldValue """ +A read and acknowledge notice if the member needs to be shown some urgent information (if water is still leaking -> turn off the pipe). Not terminal +Submitted using `Mutation.claimIntentSubmitInformation`. +""" +type ClaimIntentStepContentInformation { + notice: String! + """ + So we can show a white/red border if info/critical respectively. + """ + severity: ClaimIntentStepContentInformationSeverity! + buttonTitle: String! +} +enum ClaimIntentStepContentInformationSeverity { + INFO + CRITICAL +} +""" A select step is one that contains a choice to select one of several alternatives. It can be seen as a special-case form with nicer rendering. Submitted using `Mutation.claimIntentSubmitSelect`. @@ -1268,6 +1309,9 @@ input ClaimIntentSubmitFormInput { stepId: ID! fields: [ClaimIntentFormSubmitInputField!]! } +input ClaimIntentSubmitInformationInput { + stepId: ID! +} input ClaimIntentSubmitSelectInput { stepId: ID! selectedId: ID! @@ -1662,6 +1706,10 @@ input CrossSellInput { A/B test experiments for attribution tracking """ experiments: [CrossSellExperimentInput!]! + """ + The contract that was changed in the flow. When set, enables addon recommendations for that contract. + """ + contractId: ID } enum CrossSellSource { HOME @@ -1677,6 +1725,10 @@ type CrossSellV2 { """ recommendedCrossSell: RecommendedCrossSell """ + Addon recommendation for the contract in input.contractId (null when not applicable) + """ + recommendedAddon: RecommendedAddonCrossSell + """ Other available cross-sells (behavior varies by userFlow) """ otherCrossSells: [CrossSell!]! @@ -2433,6 +2485,28 @@ type InsuranceEvidenceOutput { insuranceEvidenceInformation: InsuranceEvidenceInformation userError: UserError } +input InsuranceGuideInput { + """ + Email of the user. + """ + email: String + """ + Id of the insurance guide shopSession. + """ + shopSessionId: UUID! + """ + Insurance recommendation form answers. + """ + answers: AnswersInput! + """ + Insurance recommendations. + """ + recommendations: [RecommendationInput!]! +} +type InsuranceGuideOutput { + success: Boolean! + userError: UserError +} input InsurelyInitiateIframeDataCollectionInput { collectionId: String! partner: String @@ -3449,6 +3523,10 @@ type MoveIntentMutationOutput { Fail case """ userError: UserError + """ + Id of the new contract if it was created after finalizing the move + """ + newContractId: ID } type MoveIntentQuoteCost { quoteId: ID! @@ -3655,6 +3733,10 @@ type Mutation { """ claimIntentSubmitTask(input: ClaimIntentSubmitTaskInput!): ClaimIntentMutationOutput! """ + Acknowledge a step containing a `ClaimIntentStepContentInformation`, continuing the flow. + """ + claimIntentSubmitInformation(input: ClaimIntentSubmitInformationInput!): ClaimIntentMutationOutput! + """ Submit a step containing a `ClaimIntentStepContentSummary`. """ claimIntentSubmitSummary(input: ClaimIntentSubmitSummaryInput!): ClaimIntentMutationOutput! @@ -3729,6 +3811,10 @@ type Mutation { """ productOffersCancellationRequestedUpdate(productOfferIds: [UUID!]!, requested: Boolean!): ProductOffersMutationOutput! """ + Called to save insurance recommendation form and save recommendation email. + """ + insuranceGuide(input: InsuranceGuideInput!): InsuranceGuideOutput! + """ Create a `PriceIntent`. The `input.productName` has to be the name of a product among the ones listed at `Query.availableProducts`. The choice of product also affects which data will be needed for the intent to be confirmed and to produce offers. @@ -4305,6 +4391,10 @@ type ProductOffer { """ priceIntentId: UUID """ + Link to the price calculator for this offer (resumes the shop session). + """ + priceCalculatorLink: String + """ 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! @@ -4706,6 +4796,28 @@ type RecommendationExternalInsurance { """ dataCollectionId: String! } +input RecommendationInput { + productId: String! + name: String! + tier: String + productPageUrl: String! + priceCalculatorUrl: String! +} +""" +A recommendation to add an addon to an existing contract. The button opens the addon flow via an app deep link. +""" +type RecommendedAddonCrossSell { + id: ID! + title: String! + description: String! + buttonTitle: String! + """ + App deep link to the addon flow for this contract + """ + deepLink: String! + pillowImageSmall: StoryblokImageAsset! + pillowImageLarge: StoryblokImageAsset! +} type RecommendedCrossSell { crossSell: CrossSell! bannerText: String! diff --git a/app/data/data-changetier/src/main/graphql/MutationChangeTierDeductible.graphql b/app/data/data-changetier/src/main/graphql/MutationChangeTierDeductible.graphql index 1de9f27fb4..0c21535700 100644 --- a/app/data/data-changetier/src/main/graphql/MutationChangeTierDeductible.graphql +++ b/app/data/data-changetier/src/main/graphql/MutationChangeTierDeductible.graphql @@ -19,6 +19,7 @@ $addonsFlagOn: Boolean!) { } tierLevel tierName + tierDescription } quotes { currentTotalCost { @@ -48,6 +49,7 @@ $addonsFlagOn: Boolean!) { } tierLevel tierName + tierDescription addons @include(if: $addonsFlagOn) { addonVariant { ...AddonVariantFragment diff --git a/app/data/data-changetier/src/main/kotlin/com/hedvig/android/data/changetier/data/CreateChangeTierDeductibleIntentUseCase.kt b/app/data/data-changetier/src/main/kotlin/com/hedvig/android/data/changetier/data/CreateChangeTierDeductibleIntentUseCase.kt index 709901bcde..91e34383cb 100644 --- a/app/data/data-changetier/src/main/kotlin/com/hedvig/android/data/changetier/data/CreateChangeTierDeductibleIntentUseCase.kt +++ b/app/data/data-changetier/src/main/kotlin/com/hedvig/android/data/changetier/data/CreateChangeTierDeductibleIntentUseCase.kt @@ -100,7 +100,7 @@ internal class CreateChangeTierDeductibleIntentUseCaseImpl( tier = Tier( tierName = tierName, tierLevel = tierLevel, - tierDescription = productVariant.tierDescription, + tierDescription = tierDescription ?: productVariant.tierDescription, tierDisplayName = productVariant.displayNameTier, ), displayItems = displayItems.toDisplayItems(), @@ -126,7 +126,7 @@ internal class CreateChangeTierDeductibleIntentUseCaseImpl( tier = Tier( tierName = it.tierName, tierLevel = it.tierLevel, - tierDescription = it.productVariant.tierDescription, + tierDescription = it.tierDescription ?: it.productVariant.tierDescription, tierDisplayName = it.productVariant.displayNameTier, ), addons = it.addons?.map { addon -> diff --git a/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectCoverageViewModel.kt b/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectCoverageViewModel.kt index cd86130499..df658b7e6b 100644 --- a/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectCoverageViewModel.kt +++ b/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectCoverageViewModel.kt @@ -219,6 +219,7 @@ internal class SelectCoveragePresenter( ), quotesForChosenTier = currentPartialStateValue.map[chosenTier]!!, isTierChoiceEnabled = currentPartialStateValue.map.keys.size > 1, + showCompareCoverage = currentPartialStateValue.map.keys.any { it.tierDisplayName != null }, contractData = currentPartialStateValue.contractData, chosenInDialogQuote = chosenQuoteInDialog, chosenInDialogTier = chosenTierInDialog, @@ -326,6 +327,8 @@ internal data class SelectCoverageSuccessUiState( val chosenInDialogQuote: TierDeductibleQuote?, val isCurrentChosen: Boolean, val isTierChoiceEnabled: Boolean, + // whether to show the compare/show-coverage button (true when any tier has a display name) + val showCompareCoverage: Boolean, // sorted list of tiers with corresponding premiums (depending on selected deductible) val tiers: List>, val quotesForChosenTier: List, diff --git a/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt b/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt index b054469c73..17826ac85c 100644 --- a/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt +++ b/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt @@ -270,14 +270,20 @@ private fun SelectTierScreen( .fillMaxWidth() .padding(horizontal = 16.dp), ) - if (uiState.tiers.size > 1) { + if (uiState.showCompareCoverage) { Spacer(Modifier.height(8.dp)) HedvigTextButton( buttonSize = Large, modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp), - text = stringResource(Res.string.TIER_FLOW_COMPARE_BUTTON), + text = stringResource( + if (uiState.tiers.size == 1) { + Res.string.TIER_FLOW_SHOW_COVERAGE_BUTTON + } else { + Res.string.TIER_FLOW_COMPARE_BUTTON + }, + ), onClick = { onCompareClick() }, @@ -753,6 +759,7 @@ private fun SelectTierScreenPreview() { ), quotesForChosenTier = listOf(quotesForPreview[0]), isTierChoiceEnabled = true, + showCompareCoverage = true, chosenTier = Tier( "BAS", tierLevel = 0, From 1a23bb5f2d2be90cf315648b1a68ba125a0ee420 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Tue, 30 Jun 2026 22:22:44 +0200 Subject: [PATCH 2/9] use tierName iftierDisplayName is null --- .../change/tier/ui/stepcustomize/SelectTierDestination.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt b/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt index 17826ac85c..7fd6c67490 100644 --- a/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt +++ b/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt @@ -95,6 +95,7 @@ import hedvig.resources.TIER_FLOW_SELECT_COVERAGE_SUBTITLE import hedvig.resources.TIER_FLOW_SELECT_COVERAGE_TITLE import hedvig.resources.TIER_FLOW_SELECT_DEDUCTIBLE_SUBTITLE import hedvig.resources.TIER_FLOW_SELECT_DEDUCTIBLE_TITLE +import hedvig.resources.TIER_FLOW_SHOW_COVERAGE_BUTTON import hedvig.resources.TIER_FLOW_SUBTITLE import hedvig.resources.TIER_FLOW_TITLE import hedvig.resources.TIER_FLOW_TOTAL @@ -333,7 +334,7 @@ private fun CustomizationCard( Spacer(Modifier.height(16.dp)) val tierSimpleItems = buildList { for (tier in tiers) { - add(SimpleDropdownItem(tier.first.tierDisplayName ?: "-")) + add(SimpleDropdownItem(tier.first.tierDisplayName ?: tier.first.tierName)) } } val hintText = stringResource(Res.string.TIER_FLOW_COVERAGE_PLACEHOLDER) @@ -549,7 +550,7 @@ private fun TierCoverageRadioGroup( options = tiers.map { pair -> RadioOption( id = RadioOptionId(pair.first.tierName), - text = pair.first.tierDisplayName ?: "-", + text = pair.first.tierDisplayName ?: pair.first.tierName, label = pair.first.tierDescription, ) }, From 9e07893c39bf1c0f6eb40079d67ac38e19cd0fa6 Mon Sep 17 00:00:00 2001 From: mariiapanasetskaia Date: Wed, 1 Jul 2026 15:07:17 +0200 Subject: [PATCH 3/9] add contract group and pillow --- .../com/hedvig/android/data/contract/ContractGroup.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/data/data-contract/src/commonMain/kotlin/com/hedvig/android/data/contract/ContractGroup.kt b/app/data/data-contract/src/commonMain/kotlin/com/hedvig/android/data/contract/ContractGroup.kt index 3f87d3382e..df27e29d40 100644 --- a/app/data/data-contract/src/commonMain/kotlin/com/hedvig/android/data/contract/ContractGroup.kt +++ b/app/data/data-contract/src/commonMain/kotlin/com/hedvig/android/data/contract/ContractGroup.kt @@ -8,6 +8,7 @@ import hedvigandroid.data_contract.generated.resources.dog import hedvigandroid.data_contract.generated.resources.home import hedvigandroid.data_contract.generated.resources.homeowner import hedvigandroid.data_contract.generated.resources.rental +import hedvigandroid.data_contract.generated.resources.safety import hedvigandroid.data_contract.generated.resources.student import hedvigandroid.data_contract.generated.resources.vacation import hedvigandroid.data_contract.generated.resources.villa @@ -25,6 +26,7 @@ enum class ContractGroup { STUDENT, COUNTRY_HOME, QASA_LANDLORD, + PAYMENT_PROTECTION, UNKNOWN, } @@ -41,6 +43,7 @@ fun ContractGroup.gradientResource(): DrawableResource = when (this) { ContractGroup.COUNTRY_HOME -> Res.drawable.vacation ContractGroup.UNKNOWN -> Res.drawable.home ContractGroup.QASA_LANDLORD -> Res.drawable.home + ContractGroup.PAYMENT_PROTECTION ->Res.drawable.safety } fun ContractGroup.pillowResource(): DrawableResource = when (this) { @@ -56,6 +59,7 @@ fun ContractGroup.pillowResource(): DrawableResource = when (this) { ContractGroup.COUNTRY_HOME -> Res.drawable.vacation ContractGroup.UNKNOWN -> Res.drawable.home ContractGroup.QASA_LANDLORD -> Res.drawable.home + ContractGroup.PAYMENT_PROTECTION ->Res.drawable.safety } fun String.toContractGroup(): ContractGroup = when (this) { @@ -132,5 +136,7 @@ fun String.toContractGroup(): ContractGroup = when (this) { "SE_QASA_LANDLORD" -> ContractGroup.QASA_LANDLORD + "SE_PAYMENT_PROTECTION" -> ContractGroup.PAYMENT_PROTECTION + else -> ContractGroup.UNKNOWN } From b16b9bc2c4b627e6a429c01a9c94235e93bded6b Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Thu, 2 Jul 2026 17:39:18 +0200 Subject: [PATCH 4/9] Download strings --- .../src/androidMain/res/values-sv-rSE/strings.xml | 4 ++-- .../src/commonMain/composeResources/values-sv-rSE/strings.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml b/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml index 75a8dca92c..ad306062ea 100644 --- a/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml +++ b/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml @@ -503,8 +503,8 @@ Inga konversationer än. Starta en genom att skriva ett meddelande Din inkorg är tom Nytt - Anmäl något som har hänt med dig eller dina saker - Frågor om din försäkring, betalningar och annat + Anmäl något som hänt dig eller dina saker + Frågor om din försäkring, betalningar eller annat Erbjudanden Du har ingen aktiv försäkring Tillägg diff --git a/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml b/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml index 2d75e502d1..91db24e845 100644 --- a/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml +++ b/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml @@ -502,8 +502,8 @@ Inga konversationer än. Starta en genom att skriva ett meddelande Din inkorg är tom Nytt - Anmäl något som har hänt med dig eller dina saker - Frågor om din försäkring, betalningar och annat + Anmäl något som hänt dig eller dina saker + Frågor om din försäkring, betalningar eller annat Erbjudanden Du har ingen aktiv försäkring Tillägg From 585f17785e6305db83f31b982b7387f3bad37afe Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Thu, 2 Jul 2026 17:44:01 +0200 Subject: [PATCH 5/9] Run ktlint --- .../kotlin/com/hedvig/android/data/contract/ContractGroup.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/data/data-contract/src/commonMain/kotlin/com/hedvig/android/data/contract/ContractGroup.kt b/app/data/data-contract/src/commonMain/kotlin/com/hedvig/android/data/contract/ContractGroup.kt index 87e51fd652..2617e07546 100644 --- a/app/data/data-contract/src/commonMain/kotlin/com/hedvig/android/data/contract/ContractGroup.kt +++ b/app/data/data-contract/src/commonMain/kotlin/com/hedvig/android/data/contract/ContractGroup.kt @@ -43,7 +43,7 @@ fun ContractGroup.gradientResource(): DrawableResource = when (this) { ContractGroup.COUNTRY_HOME -> Res.drawable.vacation ContractGroup.UNKNOWN -> Res.drawable.home ContractGroup.QASA_LANDLORD -> Res.drawable.home - ContractGroup.PAYMENT_PROTECTION ->Res.drawable.safety + ContractGroup.PAYMENT_PROTECTION -> Res.drawable.safety } fun ContractGroup.pillowResource(): DrawableResource = when (this) { @@ -59,7 +59,7 @@ fun ContractGroup.pillowResource(): DrawableResource = when (this) { ContractGroup.COUNTRY_HOME -> Res.drawable.vacation ContractGroup.UNKNOWN -> Res.drawable.home ContractGroup.QASA_LANDLORD -> Res.drawable.home - ContractGroup.PAYMENT_PROTECTION ->Res.drawable.safety + ContractGroup.PAYMENT_PROTECTION -> Res.drawable.safety } fun String.toContractGroup(): ContractGroup = when (this) { From cb8474dfbdf0ea6cd4d3b6aaa1912f2e43d77366 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Thu, 2 Jul 2026 18:22:01 +0200 Subject: [PATCH 6/9] Detect payment protection by contract group in change-tier flow Replace the incidental tierDisplayName != null heuristic with an explicit ContractGroup.PAYMENT_PROTECTION check. Payment protection reuses the tier flow only to pick an insured amount, so it has no comparable coverage and no deductible. Use the flag to hide the compare/show-coverage button and to swap in TIER_FLOW_SUBTITLE_WITHOUT_DEDUCTIBLE, which drops the deductible wording. --- .../src/androidMain/res/values-sv-rSE/strings.xml | 1 + .../src/androidMain/res/values/strings.xml | 1 + .../composeResources/values-sv-rSE/strings.xml | 1 + .../commonMain/composeResources/values/strings.xml | 1 + .../tier/ui/stepcustomize/SelectCoverageViewModel.kt | 8 +++++--- .../tier/ui/stepcustomize/SelectTierDestination.kt | 12 +++++++++--- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml b/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml index ad306062ea..671a6ac4bf 100644 --- a/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml +++ b/app/core/core-resources/src/androidMain/res/values-sv-rSE/strings.xml @@ -986,6 +986,7 @@ Välj den försäkring du vill uppdatera Visa ditt skydd Välj din skyddsnivå och självrisk + Välj din skyddsnivå Godkänn ändringar Skyddet Dokument diff --git a/app/core/core-resources/src/androidMain/res/values/strings.xml b/app/core/core-resources/src/androidMain/res/values/strings.xml index 0d194724d5..6657ce11d1 100644 --- a/app/core/core-resources/src/androidMain/res/values/strings.xml +++ b/app/core/core-resources/src/androidMain/res/values/strings.xml @@ -986,6 +986,7 @@ Select the insurance you want to edit Show coverage Set your coverage level and deductible + Set your coverage level Confirm changes Coverage Documents diff --git a/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml b/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml index 91db24e845..42758c2870 100644 --- a/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml +++ b/app/core/core-resources/src/commonMain/composeResources/values-sv-rSE/strings.xml @@ -985,6 +985,7 @@ Välj den försäkring du vill uppdatera Visa ditt skydd Välj din skyddsnivå och självrisk + Välj din skyddsnivå Godkänn ändringar Skyddet Dokument diff --git a/app/core/core-resources/src/commonMain/composeResources/values/strings.xml b/app/core/core-resources/src/commonMain/composeResources/values/strings.xml index 0061307317..6428d4a37d 100644 --- a/app/core/core-resources/src/commonMain/composeResources/values/strings.xml +++ b/app/core/core-resources/src/commonMain/composeResources/values/strings.xml @@ -985,6 +985,7 @@ Select the insurance you want to edit Show coverage Set your coverage level and deductible + Set your coverage level Confirm changes Coverage Documents diff --git a/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectCoverageViewModel.kt b/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectCoverageViewModel.kt index df658b7e6b..0e4cad32f0 100644 --- a/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectCoverageViewModel.kt +++ b/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectCoverageViewModel.kt @@ -219,7 +219,8 @@ internal class SelectCoveragePresenter( ), quotesForChosenTier = currentPartialStateValue.map[chosenTier]!!, isTierChoiceEnabled = currentPartialStateValue.map.keys.size > 1, - showCompareCoverage = currentPartialStateValue.map.keys.any { it.tierDisplayName != null }, + isPaymentProtection = + currentPartialStateValue.contractData.contractGroup == ContractGroup.PAYMENT_PROTECTION, contractData = currentPartialStateValue.contractData, chosenInDialogQuote = chosenQuoteInDialog, chosenInDialogTier = chosenTierInDialog, @@ -327,8 +328,9 @@ internal data class SelectCoverageSuccessUiState( val chosenInDialogQuote: TierDeductibleQuote?, val isCurrentChosen: Boolean, val isTierChoiceEnabled: Boolean, - // whether to show the compare/show-coverage button (true when any tier has a display name) - val showCompareCoverage: Boolean, + // Payment protection has no real tiers nor comparable coverage: it reuses the tier flow only to pick an + // insured amount. Used to hide the compare/show-coverage button and to swap the deductible-mentioning subtitle. + val isPaymentProtection: Boolean, // sorted list of tiers with corresponding premiums (depending on selected deductible) val tiers: List>, val quotesForChosenTier: List, diff --git a/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt b/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt index 7fd6c67490..c9d9b3d58d 100644 --- a/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt +++ b/app/feature/feature-choose-tier/src/main/kotlin/com/hedvig/android/feature/change/tier/ui/stepcustomize/SelectTierDestination.kt @@ -97,6 +97,7 @@ import hedvig.resources.TIER_FLOW_SELECT_DEDUCTIBLE_SUBTITLE import hedvig.resources.TIER_FLOW_SELECT_DEDUCTIBLE_TITLE import hedvig.resources.TIER_FLOW_SHOW_COVERAGE_BUTTON import hedvig.resources.TIER_FLOW_SUBTITLE +import hedvig.resources.TIER_FLOW_SUBTITLE_WITHOUT_DEDUCTIBLE import hedvig.resources.TIER_FLOW_TITLE import hedvig.resources.TIER_FLOW_TOTAL import hedvig.resources.general_cancel_button @@ -235,7 +236,12 @@ private fun SelectTierScreen( lineBreak = LineBreak.Heading, color = HedvigTheme.colorScheme.textSecondary, ), - text = stringResource(Res.string.TIER_FLOW_SUBTITLE), + // Payment protection has no deductible, so the standard "…level and deductible" subtitle over-promises. + text = if (uiState.isPaymentProtection) { + stringResource(Res.string.TIER_FLOW_SUBTITLE_WITHOUT_DEDUCTIBLE) + } else { + stringResource(Res.string.TIER_FLOW_SUBTITLE) + }, modifier = Modifier.padding(horizontal = 16.dp), ) Spacer(Modifier.weight(1f)) @@ -271,7 +277,7 @@ private fun SelectTierScreen( .fillMaxWidth() .padding(horizontal = 16.dp), ) - if (uiState.showCompareCoverage) { + if (!uiState.isPaymentProtection) { Spacer(Modifier.height(8.dp)) HedvigTextButton( buttonSize = Large, @@ -760,7 +766,7 @@ private fun SelectTierScreenPreview() { ), quotesForChosenTier = listOf(quotesForPreview[0]), isTierChoiceEnabled = true, - showCompareCoverage = true, + isPaymentProtection = false, chosenTier = Tier( "BAS", tierLevel = 0, From be359ee2a40f93d971d55ddd77f3a10432860e80 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 3 Jul 2026 15:31:24 +0200 Subject: [PATCH 7/9] Add SelectCoveragePresenter tests for payment-protection detection Cover both branches of isPaymentProtection: a PAYMENT_PROTECTION contract group resolves to true, any other group to false. --- .../SelectCoveragePresenterTest.kt | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/app/feature/feature-choose-tier/src/test/kotlin/ui/stepcustomize/SelectCoveragePresenterTest.kt b/app/feature/feature-choose-tier/src/test/kotlin/ui/stepcustomize/SelectCoveragePresenterTest.kt index 922dad5b95..682dfa6619 100644 --- a/app/feature/feature-choose-tier/src/test/kotlin/ui/stepcustomize/SelectCoveragePresenterTest.kt +++ b/app/feature/feature-choose-tier/src/test/kotlin/ui/stepcustomize/SelectCoveragePresenterTest.kt @@ -12,6 +12,7 @@ import assertk.assertions.isNull import assertk.assertions.prop import basTier import com.hedvig.android.core.common.ErrorMessage +import com.hedvig.android.data.contract.ContractGroup import com.hedvig.android.feature.change.tier.data.CurrentContractData import com.hedvig.android.feature.change.tier.data.GetCurrentContractDataUseCase import com.hedvig.android.feature.change.tier.navigation.ComparisonKey @@ -264,8 +265,56 @@ class SelectCoveragePresenterTest { .isNull() } } + + @Test + fun `when the contract is payment protection isPaymentProtection is true`() = runTest { + val tierRepo = FakeChangeTierRepository() + val presenter = SelectCoveragePresenter( + params = params, + tierRepository = tierRepo, + getCurrentContractDataUseCase = FakeGetCurrentContractDataUseCase(), + backstack = TestBackstack(), + ) + presenter.test(SelectCoverageState.Loading) { + tierRepo.quoteListTurbine.add(listOf(paymentProtectionQuote)) + skipItems(1) + assertThat(awaitItem()) + .isInstanceOf(SelectCoverageState.Success::class) + .prop(SelectCoverageState.Success::uiState) + .isInstanceOf(SelectCoverageSuccessUiState::class.java) + .prop(SelectCoverageSuccessUiState::isPaymentProtection) + .isEqualTo(true) + } + } + + @Test + fun `when the contract is not payment protection isPaymentProtection is false`() = runTest { + val tierRepo = FakeChangeTierRepository() + val presenter = SelectCoveragePresenter( + params = params, + tierRepository = tierRepo, + getCurrentContractDataUseCase = FakeGetCurrentContractDataUseCase(), + backstack = TestBackstack(), + ) + presenter.test(SelectCoverageState.Loading) { + tierRepo.quoteListTurbine.add(listOf(testQuote, testQuote2, currentQuote)) + skipItems(1) + assertThat(awaitItem()) + .isInstanceOf(SelectCoverageState.Success::class) + .prop(SelectCoverageState.Success::uiState) + .isInstanceOf(SelectCoverageSuccessUiState::class.java) + .prop(SelectCoverageSuccessUiState::isPaymentProtection) + .isEqualTo(false) + } + } } +// A current quote whose contract group is payment protection, which reuses the tier flow only to pick an +// insured amount. Its id must match FakeChangeTierRepository.getCurrentQuoteId() so it is treated as current. +private val paymentProtectionQuote = currentQuote.copy( + productVariant = currentQuote.productVariant.copy(contractGroup = ContractGroup.PAYMENT_PROTECTION), +) + private class FakeGetCurrentContractDataUseCase() : GetCurrentContractDataUseCase { override suspend fun invoke(insuranceId: String): Either { return either { CurrentContractData("exposure name") } From fd93a63c91c680430c83ad30e452f87b758d9bf5 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 3 Jul 2026 15:39:47 +0200 Subject: [PATCH 8/9] Document verifying Lokalise keys via downloadStrings in CLAUDE.md --- CLAUDE.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index b811a101c2..5390fe7ada 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -628,6 +628,13 @@ Example: Text("This is some text for feature X") ``` +**Verifying whether a string key is "real" (already in Lokalise):** if you find a key in a +`strings.xml` and are unsure whether it actually exists in Lokalise or was hand-added, run +`./gradlew downloadStrings` and re-check the file. Because `downloadStrings` regenerates every +`strings.xml` from Lokalise, a key that **survives** the run exists in Lokalise; a key that +**disappears** was only added locally and would break the build once someone else syncs. Use this +before relying on (or committing code that references) a key you didn't personally add to Lokalise. + ## Debugging ### Common Issues From 725f66518e7747fbec3dfea7c2f5815da25e3651 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 3 Jul 2026 18:31:05 +0200 Subject: [PATCH 9/9] Download schema --- .../com/hedvig/android/apollo/octopus/schema.graphqls | 9 +++++++++ 1 file changed, 9 insertions(+) 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 9b640dcbe3..98edc25fe0 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 @@ -971,6 +971,11 @@ type ClaimIntent { """ displayName: String """ + Whether this draft is resumable by the member. A draft that has been deleted (see `claimIntentDeleteDraft`) + is no longer resumable. + """ + resumable: Boolean! + """ The created claim of this intent. Once this value is returned, the intent is effectively over, and no steps can be produced or interacted with. @@ -3713,6 +3718,10 @@ type Mutation { """ claimIntentRegretStep(stepId: ID!): ClaimIntentMutationOutput! """ + Delete a draft claim intent from the member's perspective (a soft delete) so it is no longer returned by `resumableClaimIntent`. + """ + claimIntentDeleteDraft(id: ID!): Boolean! + """ Submit a step containing a `ClaimIntentStepContentForm`. """ claimIntentSubmitForm(input: ClaimIntentSubmitFormInput!): ClaimIntentMutationOutput!