diff --git a/.eslintrc.json b/.eslintrc.json index 4f495e599f14..f38cda91babc 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -311,6 +311,11 @@ { "selector": "enumMember", "format": ["camelCase", "UPPER_CASE", "PascalCase"] + }, + { + "selector": "import", + "leadingUnderscore": "allow", + "format": ["camelCase", "PascalCase"] } ], "@typescript-eslint/no-empty-interface": [ diff --git a/.github/actions/get-cache/action.yml b/.github/actions/get-cache/action.yml index a4f1184142d6..e6a561b20c58 100644 --- a/.github/actions/get-cache/action.yml +++ b/.github/actions/get-cache/action.yml @@ -34,8 +34,11 @@ runs: ENABLE_CACHE: ${{ inputs.enable-cache }} NODE_OPTIONS: --max-old-space-size=8192 run: | - echo $_CACHE_KEYS + # This is because of a weird bug where Github would write as root to .cache + export COREPACK_HOME=/github/home/.corepack-cache + export CYPRESS_CACHE_FOLDER=/github/home/.cypress-cache + echo "COREPACK_HOME=$COREPACK_HOME" >> $GITHUB_ENV + echo "CYPRESS_CACHE_FOLDER=$CYPRESS_CACHE_FOLDER" >> $GITHUB_ENV cd scripts/ci/cache - yarn install --immutable + npx yarn install --immutable node cache-action.mjs - echo $_CACHE_KEYS diff --git a/.github/workflows/config-values.yaml b/.github/workflows/config-values.yaml index 38c377878326..e8ee060b7add 100644 --- a/.github/workflows/config-values.yaml +++ b/.github/workflows/config-values.yaml @@ -74,7 +74,7 @@ jobs: uses: ./.github/actions/cache with: path: infra/node_modules - key: ${{ runner.os }}-${{ hashFiles('infra/yarn.lock') }}-infra + key: ${{ runner.os }}-${{ hashFiles('infra/yarn.lock') }}-infra-2 - name: Check cache success run: '[[ "${{ steps.node-modules.outputs.success }}" != "false" ]] || exit 1' @@ -94,8 +94,7 @@ jobs: - name: Commit any changes to charts if: ${{ github.event_name == 'pull_request' }} run: | - (cd infra && corepack enable) - yarn --cwd infra charts + (cd infra && yarn charts) ./infra/scripts/ci/git-check-dirty.sh "charts/" "charts" "dirtybot" check-secrets: diff --git a/.github/workflows/datadog-analysis.yml b/.github/workflows/datadog-analysis.yml new file mode 100644 index 000000000000..c02d93f7e3c0 --- /dev/null +++ b/.github/workflows/datadog-analysis.yml @@ -0,0 +1,44 @@ +name: Datadog Static Analysis +on: + push: + branches: + - 'main' + - 'release/**' + - 'pre-release/**' + paths-ignore: + - '**/*.md' + workflow_dispatch: + create: + pull_request: + types: + - opened + - synchronize + +jobs: + static-analysis: + runs-on: ec2-runners + container: + image: public.ecr.aws/m3u4c4h9/island-is/actions-runner-public:latest + name: Datadog Static Analyzer + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Check code meets quality and security standards + id: datadog-static-analysis + uses: DataDog/datadog-static-analyzer-github-action@v1 + with: + dd_api_key: ${{ secrets.DD_API_KEY }} + dd_app_key: ${{ secrets.DD_APP_KEY }} + dd_service: islandis + dd_env: ci + dd_site: datadoghq.eu + cpu_count: 2 + - name: Check imported libraries are secure and compliant + id: datadog-software-composition-analysis + uses: DataDog/datadog-sca-github-action@main + with: + dd_api_key: ${{ secrets.DD_API_KEY }} + dd_app_key: ${{ secrets.DD_APP_KEY }} + dd_service: islandis + dd_env: ci + dd_site: datadoghq.eu diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 9b8ba9183a8b..b2c2529b425b 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -259,7 +259,7 @@ jobs: echo "SHA='$SHA', retrived from action environment" fi echo "Using SHA='$SHA' as docker tag sha" - export DOCKER_TAG=${DOCKER_BRANCH_TAG}_${SHA:0:7}_${GITHUB_RUN_NUMBER} + export DOCKER_TAG=${DOCKER_BRANCH_TAG}_${SHA}_${GITHUB_RUN_NUMBER} echo "Docker tag will be ${DOCKER_TAG}" echo "DOCKER_TAG=${DOCKER_TAG}" >> $GITHUB_OUTPUT echo "DOCKER_TAG=$DOCKER_TAG" >> $GITHUB_ENV diff --git a/apps/judicial-system/api/src/app/modules/case/models/case.model.ts b/apps/judicial-system/api/src/app/modules/case/models/case.model.ts index 0a054c4dce2a..51ec5ec640dd 100644 --- a/apps/judicial-system/api/src/app/modules/case/models/case.model.ts +++ b/apps/judicial-system/api/src/app/modules/case/models/case.model.ts @@ -447,4 +447,10 @@ export class Case { @Field(() => String, { nullable: true }) readonly indictmentCompletedDate?: string + + @Field(() => Case, { nullable: true }) + readonly mergeCase?: Case + + @Field(() => [Case], { nullable: true }) + readonly mergedCases?: Case[] } diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts b/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts index d6e4bb100b3a..cc761d4b363a 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts @@ -73,9 +73,6 @@ export class Defendant { @Field(() => DefenderChoice, { nullable: true }) readonly defenderChoice?: DefenderChoice - @Field(() => Boolean, { nullable: true }) - readonly acceptCompensationClaim?: boolean - @Field(() => SubpoenaType, { nullable: true }) readonly subpoenaType?: SubpoenaType } diff --git a/apps/judicial-system/backend/migrations/20240706153647-update-case.js b/apps/judicial-system/backend/migrations/20240706153647-update-case.js new file mode 100644 index 000000000000..c2cc153b3fe1 --- /dev/null +++ b/apps/judicial-system/backend/migrations/20240706153647-update-case.js @@ -0,0 +1,29 @@ +'use strict' + +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.sequelize.transaction((t) => + queryInterface.addColumn( + 'case', + 'merge_case_id', + { + type: Sequelize.UUID, + references: { + model: 'case', + key: 'id', + }, + allowNull: true, + }, + { transaction: t }, + ), + ) + }, + + down: (queryInterface) => { + return queryInterface.sequelize.transaction((t) => + queryInterface.removeColumn('case', 'merge_case_id', { + transaction: t, + }), + ) + }, +} diff --git a/apps/judicial-system/backend/migrations/20240710090032-update-defendant.js b/apps/judicial-system/backend/migrations/20240710090032-update-defendant.js new file mode 100644 index 000000000000..148dec7855ef --- /dev/null +++ b/apps/judicial-system/backend/migrations/20240710090032-update-defendant.js @@ -0,0 +1,22 @@ +module.exports = { + up: (queryInterface) => { + return queryInterface.sequelize.transaction((t) => + queryInterface.removeColumn('defendant', 'accept_compensation_claim', { + transaction: t, + }), + ) + }, + down: (queryInterface, Sequelize) => { + return queryInterface.sequelize.transaction((t) => + queryInterface.addColumn( + 'defendant', + 'accept_compensation_claim', + { + type: Sequelize.BOOLEAN, + allowNull: true, + }, + { transaction: t }, + ), + ) + }, +} diff --git a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts index e73b47b71811..ab71f5d339ec 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts @@ -84,7 +84,6 @@ import { courtOfAppealsRegistrarUpdateRule, districtCourtAssistantTransitionRule, districtCourtAssistantUpdateRule, - districtCourtJudgeSignRulingRule, districtCourtJudgeTransitionRule, districtCourtJudgeUpdateRule, districtCourtRegistrarTransitionRule, @@ -765,7 +764,7 @@ export class CaseController { new CaseTypeGuard([...restrictionCases, ...investigationCases]), CaseWriteGuard, ) - @RolesRules(districtCourtJudgeSignRulingRule) + @RolesRules(districtCourtJudgeRule) @Post('case/:caseId/ruling/signature') @ApiCreatedResponse({ type: SigningServiceResponse, @@ -778,6 +777,12 @@ export class CaseController { ): Promise { this.logger.debug(`Requesting a signature for the ruling of case ${caseId}`) + if (user.id !== theCase.judgeId) { + throw new ForbiddenException( + 'A ruling must be signed by the assigned judge', + ) + } + return this.caseService.requestRulingSignature(theCase).catch((error) => { if (error instanceof DokobitError) { throw new HttpException( @@ -802,7 +807,7 @@ export class CaseController { new CaseTypeGuard([...restrictionCases, ...investigationCases]), CaseWriteGuard, ) - @RolesRules(districtCourtJudgeSignRulingRule) + @RolesRules(districtCourtJudgeRule) @Get('case/:caseId/ruling/signature') @ApiOkResponse({ type: SignatureConfirmationResponse, @@ -817,6 +822,12 @@ export class CaseController { ): Promise { this.logger.debug(`Confirming a signature for the ruling of case ${caseId}`) + if (user.id !== theCase.judgeId) { + throw new ForbiddenException( + 'A ruling must be signed by the assigned judge', + ) + } + return this.caseService.getRulingSignatureConfirmation( theCase, user, diff --git a/apps/judicial-system/backend/src/app/modules/case/case.service.ts b/apps/judicial-system/backend/src/app/modules/case/case.service.ts index 95fe5ea86d23..f0aaccb3b42d 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.service.ts @@ -299,6 +299,8 @@ export const include: Includeable[] = [ where: { commentType: { [Op.in]: commentTypes } }, }, { model: Notification, as: 'notifications' }, + { model: Case, as: 'mergeCase' }, + { model: Case, as: 'mergedCases', separate: true }, ] export const order: OrderItem[] = [ diff --git a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts index eed487be849c..5ffcac1c0125 100644 --- a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts @@ -656,12 +656,12 @@ export class InternalCaseService { theCase.judge?.nationalId === nationalId ? { name: theCase.judge?.name, - role: UserRole.DISTRICT_COURT_JUDGE, + role: theCase.judge?.role, } : theCase.registrar?.nationalId === nationalId ? { name: theCase.registrar?.name, - role: UserRole.DISTRICT_COURT_REGISTRAR, + role: theCase.registrar?.role, } : {} diff --git a/apps/judicial-system/backend/src/app/modules/case/models/case.model.ts b/apps/judicial-system/backend/src/app/modules/case/models/case.model.ts index 50901392b33a..96453bfa9e01 100644 --- a/apps/judicial-system/backend/src/app/modules/case/models/case.model.ts +++ b/apps/judicial-system/backend/src/app/modules/case/models/case.model.ts @@ -1045,4 +1045,26 @@ export class Case extends Model { }) @ApiPropertyOptional({ enum: CourtSessionType }) courtSessionType?: CourtSessionType + + /********** + * The surrogate key of the case an indictment was merged in to - only used if the has been merged + **********/ + @ForeignKey(() => Case) + @Column({ type: DataType.UUID, allowNull: true }) + @ApiPropertyOptional({ type: String }) + mergeCaseId?: string + + /********** + * The case this was merged in to - only used if the case was merged + **********/ + @BelongsTo(() => Case, 'mergeCaseId') + @ApiPropertyOptional({ type: () => Case }) + mergeCase?: Case + + // /********** + // * The cases that have been merged in to the current case - only used if the case was merged + // **********/ + @HasMany(() => Case, 'mergeCaseId') + @ApiPropertyOptional({ type: () => Case }) + mergedCases?: Case[] } diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingSignatureConfirmationRolesRules.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingSignatureConfirmationRolesRules.spec.ts index e41962b38178..a7899946e2e3 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingSignatureConfirmationRolesRules.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getRulingSignatureConfirmationRolesRules.spec.ts @@ -1,6 +1,5 @@ +import { districtCourtJudgeRule } from '../../../../guards' import { CaseController } from '../../case.controller' -import { districtCourtJudgeSignRulingRule } from '../../guards/rolesRules' - describe('CaseController - Get ruling signature confirmation rules', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let rules: any[] @@ -14,6 +13,6 @@ describe('CaseController - Get ruling signature confirmation rules', () => { it('should give permission to one roles', () => { expect(rules).toHaveLength(1) - expect(rules).toContain(districtCourtJudgeSignRulingRule) + expect(rules).toContain(districtCourtJudgeRule) }) }) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/requestRulingSignatureRolesRules.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/requestRulingSignatureRolesRules.spec.ts index 81f956445bfd..400de1843470 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/caseController/requestRulingSignatureRolesRules.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/requestRulingSignatureRolesRules.spec.ts @@ -1,6 +1,5 @@ +import { districtCourtJudgeRule } from '../../../../guards' import { CaseController } from '../../case.controller' -import { districtCourtJudgeSignRulingRule } from '../../guards/rolesRules' - describe('CaseController - Request ruling signature rules', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let rules: any[] @@ -14,6 +13,6 @@ describe('CaseController - Request ruling signature rules', () => { it('should give permission to one roles', () => { expect(rules).toHaveLength(1) - expect(rules).toContain(districtCourtJudgeSignRulingRule) + expect(rules).toContain(districtCourtJudgeRule) }) }) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentAssignedRolesToCourt.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentAssignedRolesToCourt.spec.ts index aeba60fde5c3..9188a9c8b4a5 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentAssignedRolesToCourt.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverIndictmentAssignedRolesToCourt.spec.ts @@ -30,7 +30,11 @@ describe('InternalCaseController - Deliver assigned roles for indictment case to type: CaseType.INDICTMENT, court: { name: courtName }, courtCaseNumber, - judge: { name: 'Test Dómari', nationalId: '0101010101' }, + judge: { + name: 'Test Dómari', + nationalId: '0101010101', + role: UserRole.DISTRICT_COURT_JUDGE, + }, registrar: { name: 'Test Ritari', nationalId: '0202020202' }, } as Case diff --git a/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts b/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts index 4db409aa7e19..a2e68e97a596 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/dto/updateDefendant.dto.ts @@ -89,11 +89,6 @@ export class UpdateDefendantDto { @ApiPropertyOptional({ type: Date }) readonly verdictViewDate?: Date - @IsOptional() - @IsBoolean() - @ApiPropertyOptional({ type: Boolean }) - readonly acceptCompensationClaim?: boolean - @IsOptional() @IsEnum(SubpoenaType) @ApiPropertyOptional({ enum: SubpoenaType }) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/internalDefendant.controller.ts b/apps/judicial-system/backend/src/app/modules/defendant/internalDefendant.controller.ts index 2272c3027e47..1fbefa6b9af2 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/internalDefendant.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/internalDefendant.controller.ts @@ -65,8 +65,7 @@ export class InternalDefendantController { @Patch('defense/:defendantNationalId') @ApiOkResponse({ type: Defendant, - description: - 'Assigns defense choice and compensation claim decision to defendant', + description: 'Assigns defense choice to defendant', }) async assignDefender( @Param('caseId') caseId: string, diff --git a/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts b/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts index 7d965782151a..093344f50641 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/models/defendant.model.ts @@ -124,10 +124,6 @@ export class Defendant extends Model { @ApiPropertyOptional({ type: Date }) verdictViewDate?: Date - @Column({ type: DataType.BOOLEAN, allowNull: true }) - @ApiPropertyOptional({ type: Boolean }) - acceptCompensationClaim?: boolean - @Column({ type: DataType.ENUM, allowNull: true, diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/dto/subpoena.dto.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/dto/subpoena.dto.ts index 6a1dfd44d60a..5da527ace61d 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/dto/subpoena.dto.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/dto/subpoena.dto.ts @@ -20,9 +20,4 @@ export class UpdateSubpoenaDto { @IsString() @ApiProperty({ type: String, required: false }) defenderNationalId?: string - - @IsOptional() - @IsBoolean() - @ApiProperty({ type: Boolean, required: false }) - acceptCompensationClaim?: boolean } diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts index f6a1fa53a693..0f310e71aa91 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/internal/internalCase.response.ts @@ -28,7 +28,6 @@ interface Defendant { defenderEmail?: string defenderPhoneNumber?: string defenderChoice?: DefenderChoice - acceptCompensationClaim?: boolean } interface DateLog { diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts index 7be08add3f7c..c6c022006edc 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/models/subpoena.response.ts @@ -39,9 +39,6 @@ export class SubpoenaResponse { @ApiProperty({ type: () => DefenderInfo }) defenderInfo?: DefenderInfo - @ApiProperty({ type: () => Boolean }) - acceptCompensationClaim?: boolean - static fromInternalCaseResponse( internalCase: InternalCaseResponse, defendantNationalId: string, @@ -100,7 +97,6 @@ export class SubpoenaResponse { : undefined, } : undefined, - acceptCompensationClaim: defendantInfo?.acceptCompensationClaim, } } } diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index 48d6add3c279..4f0b041ca681 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -268,5 +268,11 @@ query Case($input: CaseQueryInput!) { indictmentReviewDecision indictmentCompletedDate courtSessionType + mergeCase { + id + } + mergedCases { + id + } } } diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.strings.ts b/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.strings.ts index 49ff2c06756a..9c4a47021732 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.strings.ts +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.strings.ts @@ -93,6 +93,12 @@ export const strings = defineMessages({ description: 'Notaður sem texti í niðurfelling máls valmöguleika á Niðurstaða skrefi.', }, + merge: { + id: 'judicial.system.core:court.indictments.conclusion.merge', + defaultMessage: 'Sameinað öðru máli', + description: + 'Notaður sem texti í Sameinað valmöguleika á Niðurstaða skrefi.', + }, courtRecordTitle: { id: 'judicial.system.core:court.indictments.conclusion.court_record_title', defaultMessage: 'Þingbók', diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.tsx index 657689a9345c..6ef21ea0ca90 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.tsx @@ -234,6 +234,7 @@ const Conclusion: FC = () => { ) case CaseIndictmentRulingDecision.FINE: case CaseIndictmentRulingDecision.CANCELLATION: + case CaseIndictmentRulingDecision.MERGE: return uploadFiles.some( (file) => file.category === CaseFileCategory.COURT_RECORD && @@ -437,6 +438,21 @@ const Conclusion: FC = () => { label={formatMessage(strings.dismissal)} /> + + { + setSelectedDecision(CaseIndictmentRulingDecision.MERGE) + }} + large + backgroundColor="white" + label={formatMessage(strings.merge)} + /> + diff --git a/apps/native/app/src/screens/home/applications-module.tsx b/apps/native/app/src/screens/home/applications-module.tsx index d4fdc0f73e2f..fe39aaf0e9c4 100644 --- a/apps/native/app/src/screens/home/applications-module.tsx +++ b/apps/native/app/src/screens/home/applications-module.tsx @@ -24,6 +24,7 @@ interface ApplicationsModuleProps { loading: boolean componentId: string hideAction?: boolean + hideSeeAllButton?: boolean } export const ApplicationsModule = React.memo( @@ -32,6 +33,7 @@ export const ApplicationsModule = React.memo( loading, componentId, hideAction, + hideSeeAllButton = false, }: ApplicationsModuleProps) => { const intl = useIntl() const count = applications.length @@ -72,18 +74,12 @@ export const ApplicationsModule = React.memo( /> )) - // The RN types are not up-to-date with these props which seem to have been added in RN 71. - const imageProps = { - height: 90, - width: 42, - } - return ( navigateTo(`/applications`)}> navigateTo('/applications')} style={{ @@ -115,7 +111,7 @@ export const ApplicationsModule = React.memo( } link={ diff --git a/apps/native/app/src/screens/more/more.tsx b/apps/native/app/src/screens/more/more.tsx index b20899715aee..2d30599aaa71 100644 --- a/apps/native/app/src/screens/more/more.tsx +++ b/apps/native/app/src/screens/more/more.tsx @@ -1,9 +1,14 @@ -import { ListButton, UserCard } from '@ui' +import { FamilyMemberCard, ListButton } from '@ui' import React from 'react' import { useIntl } from 'react-intl' -import { Image, SafeAreaView, ScrollView } from 'react-native' +import { + Image, + SafeAreaView, + ScrollView, + TouchableHighlight, +} from 'react-native' import { NavigationFunctionComponent } from 'react-native-navigation' -import styled from 'styled-components/native' +import styled, { useTheme } from 'styled-components/native' import assetsIcon from '../../assets/icons/assets.png' import familyIcon from '../../assets/icons/family.png' import financeIcon from '../../assets/icons/finance.png' @@ -69,6 +74,7 @@ const { useNavigationOptions, getNavigationOptions } = export const MoreScreen: NavigationFunctionComponent = ({ componentId }) => { const authStore = useAuthStore() const intl = useIntl() + const theme = useTheme() const showFinances = useFeatureFlag('isFinancesEnabled', false) const showAirDiscount = useFeatureFlag('isAirDiscountEnabled', false) @@ -88,16 +94,19 @@ export const MoreScreen: NavigationFunctionComponent = ({ componentId }) => { }} > - navigateTo(`/personalinfo`), - }, - ]} - /> + { + navigateTo('/personalinfo') + }} + > + + { return RNAlert.alert( intl.formatMessage({ id: 'settings.security.removePasskeyPromptTitle' }), @@ -184,7 +186,47 @@ export const SettingsScreen: NavigationFunctionComponent = ({ }, }) .catch((err) => { - RNAlert.alert('Villa', err.message) + console.error(err) + RNAlert.alert( + intl.formatMessage({ + id: 'settings.communication.newNotificationsErrorTitle', + }), + intl.formatMessage({ + id: 'settings.communication.newNotificationsErrorDescription', + }), + ) + }) + } + + function updateEmailNotifications(value: boolean) { + client + .mutate({ + mutation: UpdateProfileDocument, + update(cache, { data }) { + cache.modify({ + fields: { + getUserProfile: (existing) => { + return { ...existing, ...data?.updateProfile } + }, + }, + }) + }, + variables: { + input: { + canNudge: value, + }, + }, + }) + .catch((err) => { + console.error(err) + RNAlert.alert( + intl.formatMessage({ + id: 'settings.communication.newNotificationsErrorTitle', + }), + intl.formatMessage({ + id: 'settings.communication.newNotificationsErrorDescription', + }), + ) }) } @@ -193,6 +235,7 @@ export const SettingsScreen: NavigationFunctionComponent = ({ setDocumentNotifications( userProfile.data?.getUserProfile?.documentNotifications, ) + setEmailNotifications(!!userProfile.data?.getUserProfile?.canNudge) } }, [userProfile]) @@ -298,16 +341,19 @@ export const SettingsScreen: NavigationFunctionComponent = ({ > { - updateDocumentNotifications(value) - setDocumentNotifications(value) + updateEmailNotifications(value) + setEmailNotifications(value) }} disabled={userProfile.loading && !userProfile.data} - value={documentNotifications} + value={emailNotifications} thumbColor={Platform.select({ android: theme.color.dark100 })} trackColor={{ false: theme.color.dark200, @@ -316,36 +362,21 @@ export const SettingsScreen: NavigationFunctionComponent = ({ /> } /> - {/* } - /> - - } - /> */} - - { - setAppearanceMode(value ? 'automatic' : 'light') + updateDocumentNotifications(value) + setDocumentNotifications(value) }} - value={appearanceMode === 'automatic'} + disabled={userProfile.loading && !userProfile.data} + value={documentNotifications} thumbColor={Platform.select({ android: theme.color.dark100 })} trackColor={{ false: theme.color.dark200, @@ -354,50 +385,8 @@ export const SettingsScreen: NavigationFunctionComponent = ({ /> } /> - { - clearTimeout(efficient.ts) - efficient.count = (efficient.count ?? 0) + 1 - if (efficient.count === 11) { - setAppearanceMode('efficient') - } - efficient.ts = setTimeout(() => { - efficient.count = 0 - }, 500) - }} - > - - setAppearanceMode(value ? 'dark' : 'light') - } - value={appearanceMode === 'dark'} - thumbColor={Platform.select({ android: theme.color.dark100 })} - trackColor={{ - false: theme.color.dark200, - true: theme.color.blue400, - }} - /> - } - /> - - - - {locale === 'is-IS' ? 'Íslenska' : 'English'} - - } - /> - + + + + {locale === 'is-IS' ? 'Íslenska' : 'English'} + + } + /> + diff --git a/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx b/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx index 68737de92072..aea157b4b206 100644 --- a/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx +++ b/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx @@ -75,9 +75,10 @@ export const VehicleMileageScreen: NavigationFunctionComponent<{ }, }, }) - const [postMileage] = usePostVehicleMileageMutation({ - refetchQueries: [GetVehicleMileageDocument, GetVehicleDocument], - }) + const [postMileage, { loading: loadingSubmit }] = + usePostVehicleMileageMutation({ + refetchQueries: [GetVehicleMileageDocument, GetVehicleDocument], + }) const [updateMileage] = useUpdateVehicleMileageMutation({ refetchQueries: [GetVehicleMileageDocument, GetVehicleDocument], }) @@ -317,7 +318,7 @@ export const VehicleMileageScreen: NavigationFunctionComponent<{ id: 'vehicle.mileage.inputSubmitButton', })} onPress={onSubmit} - disabled={!canRegisterMileage} + disabled={!canRegisterMileage || loadingSubmit} /> diff --git a/apps/native/app/src/screens/vehicles/vehicles.tsx b/apps/native/app/src/screens/vehicles/vehicles.tsx index f7a208c22e58..c445b50f5d54 100644 --- a/apps/native/app/src/screens/vehicles/vehicles.tsx +++ b/apps/native/app/src/screens/vehicles/vehicles.tsx @@ -20,7 +20,6 @@ import { } from '../../graphql/types/schema' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { useConnectivityIndicator } from '../../hooks/use-connectivity-indicator' -import { getRightButtons } from '../../utils/get-main-root' import { testIDs } from '../../utils/test-ids' import { VehicleItem } from './components/vehicle-item' diff --git a/apps/native/app/src/ui/index.ts b/apps/native/app/src/ui/index.ts index f42d11f5f202..5f6e09d8eb2e 100644 --- a/apps/native/app/src/ui/index.ts +++ b/apps/native/app/src/ui/index.ts @@ -19,7 +19,6 @@ export * from './lib/card/finance-status-card' export * from './lib/card/status-card' export * from './lib/card/welcome-card' export * from './lib/card/link-card' -export * from './lib/card/user-card' export * from './lib/detail/content' export * from './lib/detail/header' export * from './lib/empty-state/empty-card' diff --git a/apps/native/app/src/ui/lib/card/card.stories.tsx b/apps/native/app/src/ui/lib/card/card.stories.tsx index 32c0f6e86602..212c78ec751e 100644 --- a/apps/native/app/src/ui/lib/card/card.stories.tsx +++ b/apps/native/app/src/ui/lib/card/card.stories.tsx @@ -16,7 +16,6 @@ import { FamilyMemberCard } from './family-member-card' import { LicenseCard } from './license-card' import { NotificationCard } from './notification-card' import { StatusCard } from './status-card' -import { UserCard } from './user-card' import { VehicleCard } from './vehicle-card' import { WelcomeCard } from './welcome-card' @@ -115,20 +114,6 @@ storiesOf('Cards', module) ) }) - .add('User Card', () => { - const userName = text('User name', 'Jón Jónsson') - const ssn = text('Ssn number', '010125-4529') - const actionTitle = text('Action Title', 'Skoða upplýsingar') - return ( - console.log('Action press') }, - ]} - /> - ) - }) .add('Notification Card With Actions', () => { const title = text('Notification Card Title', 'Ökuskýrteini') const message = text( diff --git a/apps/native/app/src/ui/lib/card/family-member-card.tsx b/apps/native/app/src/ui/lib/card/family-member-card.tsx index 4ffc66cd879f..8163abf80470 100644 --- a/apps/native/app/src/ui/lib/card/family-member-card.tsx +++ b/apps/native/app/src/ui/lib/card/family-member-card.tsx @@ -3,14 +3,13 @@ import { Image } from 'react-native' import styled from 'styled-components/native' import chevronForward from '../../assets/icons/chevron-forward.png' import { dynamicColor } from '../../utils' -import { font } from '../../utils/font' import { Avatar } from '../avatar/avatar' +import { Typography } from '../typography/typography' const Host = styled.View` display: flex; flex-direction: row; - padding: ${({ theme }) => theme.spacing[3]}px; - padding-right: ${({ theme }) => theme.spacing[1]}px; + padding: ${({ theme }) => theme.spacing[2]}px; border-radius: ${({ theme }) => theme.border.radius.large}; border-width: ${({ theme }) => theme.border.width.standard}px; border-color: ${dynamicColor( @@ -29,28 +28,16 @@ const Content = styled.View` ` const ImageWrap = styled.View` - margin-right: ${({ theme }) => theme.spacing[3]}px; + margin-right: ${({ theme }) => theme.spacing[2]}px; ` -const Title = styled.Text` +const Title = styled(Typography)` padding-right: ${({ theme }) => theme.spacing[1]}px; - margin-bottom: ${({ theme }) => theme.spacing[1]}px; - - ${font({ - fontWeight: '600', - lineHeight: 24, - fontSize: 18, - })} + margin-bottom: ${({ theme }) => theme.spacing.smallGutter}px; ` -const Text = styled.Text` +const Text = styled(Typography)` padding-right: ${({ theme }) => theme.spacing[2]}px; - - ${font({ - fontWeight: '300', - lineHeight: 24, - fontSize: 16, - })} ` const Icon = styled.View` @@ -65,11 +52,13 @@ interface FamilyMemberCardProps { export function FamilyMemberCard({ name, nationalId }: FamilyMemberCardProps) { return ( - - - + {name.length ? ( + + + + ) : null} - {name} + {name} {nationalId} diff --git a/apps/native/app/src/ui/lib/card/user-card.tsx b/apps/native/app/src/ui/lib/card/user-card.tsx deleted file mode 100644 index 28ae54a8b383..000000000000 --- a/apps/native/app/src/ui/lib/card/user-card.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import React from 'react' -import styled from 'styled-components/native' -import { dynamicColor } from '../../utils/dynamic-color' -import { font } from '../../utils/font' -import { Avatar } from '../avatar/avatar' - -const Host = styled.View` - width: 100%; - border-radius: ${({ theme }) => theme.border.radius.large}; - border-width: ${({ theme }) => theme.border.width.standard}px; - border-color: ${dynamicColor( - ({ theme }) => ({ - dark: theme.shades.dark.shade300, - light: theme.color.blue200, - }), - true, - )}; - margin-bottom: ${({ theme }) => theme.spacing[2]}px; -` - -const Content = styled.View` - padding: ${({ theme }) => theme.spacing[3]}px; - justify-content: center; - align-items: center; -` - -const Name = styled.Text` - margin-top: ${({ theme }) => theme.spacing[2]}px; - margin-bottom: ${({ theme }) => theme.spacing[1]}px; - - ${font({ - fontWeight: '600', - fontSize: 20, - lineHeight: 26, - })} -` - -const Ssn = styled.Text` - ${font({ - fontWeight: '300', - lineHeight: 24, - })} -` - -const ActionsContainer = styled.View` - border-top-width: ${({ theme }) => theme.border.width.standard}px; - border-top-color: ${dynamicColor( - (props) => ({ - light: props.theme.color.blue200, - dark: 'shade300', - }), - true, - )}; - flex-direction: row; -` - -const ActionButton = styled.TouchableOpacity<{ border: boolean }>` - flex: 1; - align-items: center; - justify-content: center; - padding: ${({ theme }) => theme.spacing[2]}px; - border-left-width: ${({ theme }) => theme.border.width.standard}px; - border-left-color: ${dynamicColor( - ({ theme, border }) => (!border ? 'transparent' : theme.color.blue200), - true, - )}; -` - -const ActionText = styled.Text` - ${font({ - fontWeight: '600', - color: ({ theme }) => theme.color.blue400, - })} - text-align: center; -` - -interface UserCardProps { - name?: string - ssn?: string - actions: Array<{ text: string; onPress(): void }> -} - -export function UserCard({ name, ssn, actions = [] }: UserCardProps) { - return ( - - - {name && } - {name} - {ssn} - - {actions.length ? ( - - {actions.map(({ text, onPress }, i) => ( - - {text} - - ))} - - ) : null} - - ) -} diff --git a/apps/native/app/src/ui/lib/card/vehicle-card.tsx b/apps/native/app/src/ui/lib/card/vehicle-card.tsx index 391fd831129e..6687b6e2eb20 100644 --- a/apps/native/app/src/ui/lib/card/vehicle-card.tsx +++ b/apps/native/app/src/ui/lib/card/vehicle-card.tsx @@ -3,7 +3,7 @@ import { Image } from 'react-native' import styled from 'styled-components/native' import chevronForward from '../../assets/icons/chevron-forward.png' import { dynamicColor } from '../../utils' -import { font } from '../../utils/font' +import { Typography } from '../typography/typography' const Host = styled.View` display: flex; @@ -28,32 +28,23 @@ const Content = styled.View` align-items: flex-start; ` -const Title = styled.Text` +const Title = styled(Typography)` padding-right: ${({ theme }) => theme.spacing[1]}px; margin-bottom: ${({ theme }) => theme.spacing[1]}px; - - ${font({ - fontWeight: '600', - lineHeight: 24, - fontSize: 18, - })} ` -const Text = styled.Text` +const Text = styled(Typography)` padding-right: ${({ theme }) => theme.spacing[2]}px; - margin-bottom: ${({ theme }) => theme.spacing[2]}px; - - ${font({ - fontWeight: '300', - lineHeight: 24, - fontSize: 16, - })} ` const Icon = styled.View` margin-left: auto; ` +const Label = styled.View` + margin-top: ${({ theme }) => theme.spacing[2]}px; +` + interface VehicleCardProps { title?: string | null color?: string | null @@ -65,11 +56,11 @@ export function VehicleCard({ title, color, number, label }: VehicleCardProps) { return ( - {title} + {title} {color} - {number} - {label} + {label && } diff --git a/apps/native/app/src/utils/get-main-root.ts b/apps/native/app/src/utils/get-main-root.ts index 76f5905847e0..029902621a47 100644 --- a/apps/native/app/src/utils/get-main-root.ts +++ b/apps/native/app/src/utils/get-main-root.ts @@ -25,7 +25,7 @@ export const getRightButtons = ({ }: RightButtonProps = {}): OptionsTopBarButton[] => { const iconBackground = { color: 'transparent', - cornerRadius: 8, + cornerRadius: 4, width: theme.spacing[4], height: theme.spacing[4], } diff --git a/apps/native/app/src/utils/get-theme-with-preferences.ts b/apps/native/app/src/utils/get-theme-with-preferences.ts index 6ffa91f9579e..ebc84f0dace3 100644 --- a/apps/native/app/src/utils/get-theme-with-preferences.ts +++ b/apps/native/app/src/utils/get-theme-with-preferences.ts @@ -55,9 +55,12 @@ export function getThemeWithPreferences( ) { // get color scheme from system if "automatic" // otherwise from appearanceMode - const colorScheme = ( - appearanceMode === 'automatic' ? systemScheme : appearanceMode - ) as ThemeMode + // const colorScheme = ( + // appearanceMode === 'automatic' ? systemScheme : appearanceMode + // ) as ThemeMode + + // Force light mode for now until we have a proper dark mode + const colorScheme = 'light' as ThemeMode // find correct shades key const themeKey = diff --git a/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.model.ts b/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.model.ts index e91bf7dbeb1a..b67a6c26c42d 100644 --- a/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.model.ts +++ b/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.model.ts @@ -96,6 +96,9 @@ export class EndorsementList extends Model { @HasMany(() => Endorsement) endorsements?: Endorsement[] + @ApiProperty() + endorsementCounter?: number + @ApiProperty() @Column({ type: DataType.JSONB, diff --git a/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.service.ts b/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.service.ts index 8cef2a0cc416..d5696e1155cb 100644 --- a/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.service.ts +++ b/apps/services/endorsements/api/src/app/modules/endorsementList/endorsementList.service.ts @@ -5,7 +5,7 @@ import { BadRequestException, } from '@nestjs/common' import { InjectModel } from '@nestjs/sequelize' -import { Op } from 'sequelize' +import { Op, Sequelize } from 'sequelize' import type { Logger } from '@island.is/logging' import { LOGGER_PROVIDER } from '@island.is/logging' import { EndorsementList } from './endorsementList.model' @@ -76,8 +76,28 @@ export class EndorsementListService { after: query.after, before: query.before, primaryKeyField: 'counter', - orderOption: [['counter', 'DESC']], + orderOption: [ + ['endorsementCounter', 'DESC'], + ['counter', 'DESC'], + ], where: where, + attributes: { + include: [ + [ + Sequelize.fn('COUNT', Sequelize.col('endorsements.id')), + 'endorsementCounter', + ], + ], + }, + include: [ + { + model: Endorsement, + required: false, // Required false for left outer join so that counts come for 0 as well + duplicating: false, + attributes: [], + }, + ], + group: ['EndorsementList.id'], }) } diff --git a/apps/web/components/OfficialJournalOfIceland/OJOIHomeIntro.tsx b/apps/web/components/OfficialJournalOfIceland/OJOIHomeIntro.tsx index d4978dd4cbcb..2a1ac7bdffb4 100644 --- a/apps/web/components/OfficialJournalOfIceland/OJOIHomeIntro.tsx +++ b/apps/web/components/OfficialJournalOfIceland/OJOIHomeIntro.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react' +import { ReactNode } from 'react' import { Box, diff --git a/apps/web/components/OfficialJournalOfIceland/OJOISearchGridView.tsx b/apps/web/components/OfficialJournalOfIceland/OJOISearchGridView.tsx index f4c669f98144..b926848235ba 100644 --- a/apps/web/components/OfficialJournalOfIceland/OJOISearchGridView.tsx +++ b/apps/web/components/OfficialJournalOfIceland/OJOISearchGridView.tsx @@ -1,6 +1,5 @@ -import { Locale } from '@island.is/shared/types' - import { Stack } from '@island.is/island-ui/core' +import { Locale } from '@island.is/shared/types' import { OfficialJournalOfIcelandAdvertsResponse } from '@island.is/web/graphql/schema' import { useLinkResolver } from '@island.is/web/hooks' diff --git a/apps/web/components/OfficialJournalOfIceland/OJOISearchListView.tsx b/apps/web/components/OfficialJournalOfIceland/OJOISearchListView.tsx index 958503d1b716..8cbcf8420a1e 100644 --- a/apps/web/components/OfficialJournalOfIceland/OJOISearchListView.tsx +++ b/apps/web/components/OfficialJournalOfIceland/OJOISearchListView.tsx @@ -1,8 +1,8 @@ import format from 'date-fns/format' import is from 'date-fns/locale/is' -import { Locale } from '@island.is/shared/types' import { LinkV2, Table as T, Text } from '@island.is/island-ui/core' +import { Locale } from '@island.is/shared/types' import { OfficialJournalOfIcelandAdvertsResponse } from '@island.is/web/graphql/schema' import { useLinkResolver } from '@island.is/web/hooks' diff --git a/apps/web/components/OfficialJournalOfIceland/OJOIWrapper.tsx b/apps/web/components/OfficialJournalOfIceland/OJOIWrapper.tsx index 4d72479367c4..3c4a0918b500 100644 --- a/apps/web/components/OfficialJournalOfIceland/OJOIWrapper.tsx +++ b/apps/web/components/OfficialJournalOfIceland/OJOIWrapper.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useEffect, useState } from 'react' +import { ReactNode, useEffect, useState } from 'react' import { useWindowSize } from 'react-use' import NextLink from 'next/link' @@ -12,7 +12,7 @@ import { } from '@island.is/island-ui/core' import { theme } from '@island.is/island-ui/theme' import { Footer as WebFooter } from '@island.is/web/components' -import { Image, Organization } from '@island.is/web/graphql/schema' +import { Organization } from '@island.is/web/graphql/schema' import { usePlausiblePageview } from '@island.is/web/hooks' import SidebarLayout from '@island.is/web/screens/Layouts/SidebarLayout' @@ -135,12 +135,11 @@ export const OJOIWrapper = ({ {!sidebarContent && children} - + diff --git a/apps/web/screens/OfficialJournalOfIceland/OJOIAdvert.tsx b/apps/web/screens/OfficialJournalOfIceland/OJOIAdvert.tsx index 92fcc0abc82c..2709b2766eae 100644 --- a/apps/web/screens/OfficialJournalOfIceland/OJOIAdvert.tsx +++ b/apps/web/screens/OfficialJournalOfIceland/OJOIAdvert.tsx @@ -1,7 +1,7 @@ import { useIntl } from 'react-intl' -import { Locale } from '@island.is/shared/types' import { Box, Button, Link, Stack, Text } from '@island.is/island-ui/core' +import { Locale } from '@island.is/shared/types' import { ContentLanguage, CustomPageUniqueIdentifier, diff --git a/apps/web/screens/OfficialJournalOfIceland/OJOICategories.tsx b/apps/web/screens/OfficialJournalOfIceland/OJOICategories.tsx index b03b4338998e..519fbbc80e87 100644 --- a/apps/web/screens/OfficialJournalOfIceland/OJOICategories.tsx +++ b/apps/web/screens/OfficialJournalOfIceland/OJOICategories.tsx @@ -1,6 +1,5 @@ import { useCallback, useEffect, useMemo, useState } from 'react' import { useIntl } from 'react-intl' -import { Locale } from '@island.is/shared/types' import debounce from 'lodash/debounce' import { useRouter } from 'next/router' @@ -18,6 +17,7 @@ import { Text, } from '@island.is/island-ui/core' import { debounceTime } from '@island.is/shared/constants' +import { Locale } from '@island.is/shared/types' import { ContentLanguage, CustomPageUniqueIdentifier, diff --git a/apps/web/screens/OfficialJournalOfIceland/OJOIHome.tsx b/apps/web/screens/OfficialJournalOfIceland/OJOIHome.tsx index 2cb2b12905fd..bce39b678cce 100644 --- a/apps/web/screens/OfficialJournalOfIceland/OJOIHome.tsx +++ b/apps/web/screens/OfficialJournalOfIceland/OJOIHome.tsx @@ -1,5 +1,4 @@ import { useIntl } from 'react-intl' -import { Locale } from '@island.is/shared/types' import NextLink from 'next/link' import { @@ -13,6 +12,7 @@ import { Stack, Text, } from '@island.is/island-ui/core' +import { Locale } from '@island.is/shared/types' import { SLICE_SPACING } from '@island.is/web/constants' import { ContentLanguage, diff --git a/apps/web/screens/OfficialJournalOfIceland/OJOISearch.tsx b/apps/web/screens/OfficialJournalOfIceland/OJOISearch.tsx index 5d248425a3a7..c253999cd919 100644 --- a/apps/web/screens/OfficialJournalOfIceland/OJOISearch.tsx +++ b/apps/web/screens/OfficialJournalOfIceland/OJOISearch.tsx @@ -1,6 +1,5 @@ import { useEffect, useMemo, useState } from 'react' import { useIntl } from 'react-intl' -import { Locale } from '@island.is/shared/types' import debounce from 'lodash/debounce' import { useRouter } from 'next/router' import { useLazyQuery } from '@apollo/client' @@ -16,6 +15,7 @@ import { Text, } from '@island.is/island-ui/core' import { debounceTime } from '@island.is/shared/constants' +import { Locale } from '@island.is/shared/types' import { ContentLanguage, CustomPageUniqueIdentifier, @@ -487,7 +487,7 @@ OJOISearch.getProps = async ({ apolloClient, locale }) => { } return { - initialAdverts: officialJournalOfIcelandAdverts.adverts, + initialAdverts: officialJournalOfIcelandAdverts?.adverts, categories: officialJournalOfIcelandCategories?.categories, departments: officialJournalOfIcelandDepartments?.departments, types: officialJournalOfIcelandTypes?.types, diff --git a/libs/api/domains/endorsement-system/src/lib/models/endorsementList.model.ts b/libs/api/domains/endorsement-system/src/lib/models/endorsementList.model.ts index 7c60ea4a7216..2c03818d359a 100644 --- a/libs/api/domains/endorsement-system/src/lib/models/endorsementList.model.ts +++ b/libs/api/domains/endorsement-system/src/lib/models/endorsementList.model.ts @@ -39,4 +39,7 @@ export class EndorsementList { @Field({ nullable: true }) owner?: string + + @Field({ nullable: true }) + endorsementCounter?: number } diff --git a/libs/island-ui/core/src/lib/Navigation/Navigation.tsx b/libs/island-ui/core/src/lib/Navigation/Navigation.tsx index 3799a27ea05f..f2b91183a978 100644 --- a/libs/island-ui/core/src/lib/Navigation/Navigation.tsx +++ b/libs/island-ui/core/src/lib/Navigation/Navigation.tsx @@ -151,7 +151,6 @@ export const Navigation: FC> = ({ title = 'Efnisyfirlit', titleLink, titleIcon, - activeItemTitle, label, colorScheme = 'blue', @@ -168,7 +167,28 @@ export const Navigation: FC> = ({ mobileNavigationButtonCloseLabel = 'Close', }) => { const [mobileMenuOpen, setMobileMenuOpen] = useState(false) - const [activeAccordions, setActiveAccordions] = useState>([]) + + const [activeAccordions, setActiveAccordions] = useState>( + () => { + const initialActivePathIndex = items?.findIndex( + (item) => item.active && item.accordion, + ) + + if (initialActivePathIndex > 0) { + //first level only + return [ + `1-${items?.findIndex( + (item) => + item.active && + item.accordion && + item.items?.some((child) => child.active), + )}`, + ] + } + + return [] + }, + ) const color = colorSchemeColors[colorScheme]['color'] const activeColor = colorSchemeColors[colorScheme]['activeColor'] diff --git a/libs/judicial-system/types/src/lib/case.ts b/libs/judicial-system/types/src/lib/case.ts index ce77ee069b5b..9d57c7b87679 100644 --- a/libs/judicial-system/types/src/lib/case.ts +++ b/libs/judicial-system/types/src/lib/case.ts @@ -241,6 +241,7 @@ export enum CaseIndictmentRulingDecision { FINE = 'FINE', DISMISSAL = 'DISMISSAL', CANCELLATION = 'CANCELLATION', + MERGE = 'MERGE', } export enum IndictmentCaseReviewDecision { diff --git a/libs/nest/pagination/src/lib/paginate.ts b/libs/nest/pagination/src/lib/paginate.ts index 368deefebcba..e289af92c053 100644 --- a/libs/nest/pagination/src/lib/paginate.ts +++ b/libs/nest/pagination/src/lib/paginate.ts @@ -179,8 +179,26 @@ export async function paginate({ const [instances, totalCount, cursorCount] = await Promise.all([ Model.findAll(paginationQueryOptions), - Model.count(totalCountQueryOptions), - Model.count(cursorCountQueryOptions), + Model.count(totalCountQueryOptions).then( + // If the query does relation aggregations, then the count will be an array. + // Since we are only interested in the list length, not related elements, we + // can just take the length of the array. + + (count: Array | number) => { + if (Array.isArray(count)) { + return count.length + } + return count + }, + ), + Model.count(cursorCountQueryOptions).then( + (count: Array | number) => { + if (Array.isArray(count)) { + return count.length + } + return count + }, + ), ]) if (before) { diff --git a/libs/service-portal/core/src/components/ActionCard/ActionCard.tsx b/libs/service-portal/core/src/components/ActionCard/ActionCard.tsx index b16c3c091f66..b2b5bb7f109d 100644 --- a/libs/service-portal/core/src/components/ActionCard/ActionCard.tsx +++ b/libs/service-portal/core/src/components/ActionCard/ActionCard.tsx @@ -339,13 +339,13 @@ export const ActionCard: React.FC> = ({ {/* Checking image type so the image is placed correctly */} {image?.type !== 'logo' && renderImage()} - {heading && ( - + + {heading && ( > = ({ {heading} - - {secondaryTag && ( - - {!date && !eyebrow && renderTag(secondaryTag)} - - )} - {!date && !eyebrow && renderTag(tag)} - - - )} - {text && {text}} + )} + {!heading && text && ( + {text} + )} + + {secondaryTag && ( + + {!date && !eyebrow && renderTag(secondaryTag)} + + )} + {!date && !eyebrow && renderTag(tag)} + + + {heading && text && {text}} {subText && {subText}} , }, + { + name: m.financeTransactions, + path: FinancePaths.FinanceTransactions, + enabled: true, + element: ( + + ), + }, { name: m.financeBills, path: FinancePaths.FinanceBills, diff --git a/libs/service-portal/finance/src/lib/navigation.ts b/libs/service-portal/finance/src/lib/navigation.ts index 1b1755bb5ed7..3d2a821fddaf 100644 --- a/libs/service-portal/finance/src/lib/navigation.ts +++ b/libs/service-portal/finance/src/lib/navigation.ts @@ -24,7 +24,7 @@ export const financeNavigation: PortalNavigationItem = { { name: m.financeTransactionsCategories, description: m.financeTransactionsDescription, - path: FinancePaths.FinanceTransactions, + path: FinancePaths.FinanceTransactionCategories, }, { name: m.financeTransactionPeriods, diff --git a/libs/service-portal/finance/src/lib/paths.ts b/libs/service-portal/finance/src/lib/paths.ts index f7774e56d9da..ec5f1eb5c1d3 100644 --- a/libs/service-portal/finance/src/lib/paths.ts +++ b/libs/service-portal/finance/src/lib/paths.ts @@ -2,6 +2,7 @@ export enum FinancePaths { FinanceRoot = '/fjarmal', FinanceStatus = '/fjarmal/stada', FinanceTransactions = '/fjarmal/faerslur', + FinanceTransactionCategories = '/fjarmal/faerslur/flokkar', FinanceTransactionPeriods = '/fjarmal/faerslur/timabil', FinanceEmployeeClaims = '/fjarmal/laungreidendakrofur', FinanceLocalTax = '/fjarmal/utsvar', diff --git a/libs/service-portal/finance/src/module.tsx b/libs/service-portal/finance/src/module.tsx index d7fca17b5dea..a766755042d0 100644 --- a/libs/service-portal/finance/src/module.tsx +++ b/libs/service-portal/finance/src/module.tsx @@ -9,7 +9,9 @@ import { redirects } from './financeRedirects' const FinanceStatus = lazy(() => import('./screens/FinanceStatus')) const FinanceBills = lazy(() => import('./screens/FinanceBills')) -const FinanceTransactions = lazy(() => import('./screens/FinanceTransactions')) +const FinanceTransactionsCategories = lazy(() => + import('./screens/FinanceTransactions'), +) const FinanceTransactionPeriods = lazy(() => import('./screens/FinanceTransactionPeriods'), ) @@ -48,9 +50,9 @@ export const financeModule: PortalModule = { element: , }, { - name: m.financeTransactions, - path: FinancePaths.FinanceTransactions, - element: , + name: m.financeTransactionsCategories, + path: FinancePaths.FinanceTransactionCategories, + element: , enabled: userInfo.scopes.includes(ApiScope.financeOverview), dynamic: true, loader: financeRoutesLoader({ userInfo, ...rest }), diff --git a/libs/service-portal/petitions/src/screens/Petitions/index.tsx b/libs/service-portal/petitions/src/screens/Petitions/index.tsx index e0d910372318..4373fbf948a3 100644 --- a/libs/service-portal/petitions/src/screens/Petitions/index.tsx +++ b/libs/service-portal/petitions/src/screens/Petitions/index.tsx @@ -125,7 +125,6 @@ const Petitions = () => { )} - {openSignedLists && openSignedLists.length > 0 && ( diff --git a/libs/shared/connected/src/lib/generalPetition/GeneralPetitionLists/useGetPetitionLists.ts b/libs/shared/connected/src/lib/generalPetition/GeneralPetitionLists/useGetPetitionLists.ts index 6e476f71c4ae..39105b147f1a 100644 --- a/libs/shared/connected/src/lib/generalPetition/GeneralPetitionLists/useGetPetitionLists.ts +++ b/libs/shared/connected/src/lib/generalPetition/GeneralPetitionLists/useGetPetitionLists.ts @@ -28,6 +28,7 @@ const GetGeneralPetitionLists = gql` description closedDate openedDate + endorsementCounter adminLock meta owner diff --git a/scripts/ci/00_prepare-base-tags.sh b/scripts/ci/00_prepare-base-tags.sh index 4231289a1658..bfc83b8a63bd 100755 --- a/scripts/ci/00_prepare-base-tags.sh +++ b/scripts/ci/00_prepare-base-tags.sh @@ -24,7 +24,7 @@ if [[ "$BUILD_REF" != "$LAST_GOOD_BUILD_SHA" ]]; then git log -1 "$BUILD_REF" fi LAST_GOOD_BUILD_DOCKER_BRANCH_TAG=$(echo "${LAST_GOOD_BUILD_BRANCH}" | tr "/." "-" ) -export LAST_GOOD_BUILD_DOCKER_TAG=${LAST_GOOD_BUILD_DOCKER_BRANCH_TAG:0:45}_${LAST_GOOD_BUILD_SHA:0:7}_${LAST_GOOD_BUILD_RUN_NUMBER} +export LAST_GOOD_BUILD_DOCKER_TAG=${LAST_GOOD_BUILD_DOCKER_BRANCH_TAG:0:45}_${LAST_GOOD_BUILD_SHA}_${LAST_GOOD_BUILD_RUN_NUMBER} if [[ "$BUILD_REF" == "null" || "$BUILD_REF" == "" ]]; then curl -X POST -H 'Content-type: application/json' --data "{\"text\":\"Change detection failed for $HTML_URL\"}" "$ISSUE_REPORTING_SLACK_WEBHOOK_URL" exit 1 diff --git a/scripts/ci/30_test.sh b/scripts/ci/30_test.sh index 58a6e75ac860..1d760b6e021d 100755 --- a/scripts/ci/30_test.sh +++ b/scripts/ci/30_test.sh @@ -9,11 +9,23 @@ set -euxo pipefail : "${DD_SERVICE:=${APP:-"unit-test"}}" : "${DD_API_KEY:=''}" : "${NODE_OPTIONS:=}" +: "${FLAKY_TEST_RETRIES:=3}" # Default to big old-space, and more options for testing, but allow overriding NODE_OPTIONS="--max-old-space-size=8193 --unhandled-rejections=warn --require=dd-trace/ci/init ${NODE_OPTIONS:-}" EXTRA_OPTS="" +FLAKY_TESTS=( + "services-auth-delegation-api" + "services-auth-personal-representative" +) +if [[ " ${FLAKY_TESTS[*]} " == *" ${APP} "* ]]; then + IS_FLAKY_TEST=true +else + IS_FLAKY_TEST=false +fi + + projects_uncollectible_coverage=( "application-templates-no-debt-certificate" "api-domains-email-signup" @@ -34,9 +46,12 @@ export DD_CIVISIBILITY_AGENTLESS_ENABLED \ NODE_OPTIONS \ SERVERSIDE_FEATURES_ON=\"\" # disable server-side features -yarn run test \ - "${APP}" \ - ${EXTRA_OPTS} \ - --verbose \ - --no-watchman \ - "$@" +FLAKY_TEST_RETRIES=$(if [[ "$IS_FLAKY_TEST" == true ]]; then echo "$FLAKY_TEST_RETRIES"; else echo 1; fi) + +for ((i=1; i<=FLAKY_TEST_RETRIES; i++)); do + echo "Running test ${APP} (attempt: ${i}/${FLAKY_TEST_RETRIES})" + if yarn run test "${APP}" ${EXTRA_OPTS} --verbose --no-watchman "$@"; then + exit 0 + fi +done +exit 1 \ No newline at end of file diff --git a/scripts/ci/_chunk.js b/scripts/ci/_chunk.js index f44c4cd93142..a9d0269538e3 100644 --- a/scripts/ci/_chunk.js +++ b/scripts/ci/_chunk.js @@ -5,6 +5,8 @@ const projects = process.argv[2].split(',').map((s) => s.trim()) ?? [] const problematicProjects = [ 'judicial-system-backend', 'application-system-api', + 'services-auth-delegation-api', + 'services-auth-personal-representative', ] function groupbByPrefix(arr) { diff --git a/scripts/ci/cache/__config.mjs b/scripts/ci/cache/__config.mjs index 6552ec71b40f..608ed52f7696 100644 --- a/scripts/ci/cache/__config.mjs +++ b/scripts/ci/cache/__config.mjs @@ -33,7 +33,7 @@ export const ENABLED_MODULES = (process.env[ENV_ENABLED_CACHE] || '') return a }, {}) -export const cypressPath = '/github/home/.cache/Cypress' +export const cypressPath = '/github/home/.cypress-cache' export const cacheSuccess = JSON.parse(process.env[ENV_CACHE_SUCCESS] ?? '{}') export const initCache = process.env[ENV_INIT_CACHE] === 'true' @@ -147,7 +147,7 @@ export const caches = [ } const pkg = await getPackageJSON() const cypressVersion = pkg?.devDependencies?.cypress - return `cypress-cache-${HASH_VERSION}-${getPlatformString()}-${cypressVersion}` + return `cypress-cache-${HASH_VERSION}-${getPlatformString()}-${cypressVersion}-2` }, name: 'Cache Cypress', id: 'cypress', @@ -167,6 +167,7 @@ export const caches = [ const cypressVersion = pkg?.devDependencies?.cypress await runCommand('npx cypress install', ROOT, { CYPRESS_INSTALL_BINARY: cypressVersion, + CYPRESS_CACHE_FOLDER: cypressPath, }) }, path: cypressPath || '',