Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,24 +63,16 @@ function changeModel() {
citizen.value = {
id: '05954ccb-188b-4e56-bba8-53211d60e343',
name: 'Dr. Monique Orn',
gender: 'F',
gender: null,
cpf_responsible: null,
mother_name: 'Jaime Powlowski DDS',
cpf: '56826137918',
cns: '440541140933040',
birth_date: '1946-10-02',
birth_date: null,
phone: '38 4017-7733',
cellphone: '83 7530-1849',
email: 'joaopaulosncastro@sysvale.com.br',
address: {
cep: '44443',
street: 'Jakubowski Manors',
number: '625',
complement: 'Suite 152',
neighborhood: 'Shanahan Glen',
city: 'Albinborough',
uf: 'NY',
},
address: null,
race: 'black',
co_cidadao: 2852,
is_dead: true,
Expand Down
54 changes: 54 additions & 0 deletions src/components/CitizenSummaryViewer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ const femaleCitizen: Partial<Citizen> = {
mother_name: 'Mary Doe',
};

const incompleteCitizen: Partial<Citizen> = {
...maleCitizen,
mother_name: '',
};

const stubs = {
CdsText: true,
CdsBadge: true,
Expand Down Expand Up @@ -83,6 +88,8 @@ describe('CitizenSummaryViewer', () => {

expect(wrapper.findComponent('[data-testid="pregnant-badge"]').exists()).toBeFalsy();
expect(pregnantCitizenWrapper.findComponent('[data-testid="pregnant-badge"]').exists()).toBeTruthy();

pregnantCitizenWrapper.unmount();
});

test('actions are hidden accordingly to props', async () => {
Expand Down Expand Up @@ -128,4 +135,51 @@ describe('CitizenSummaryViewer', () => {
closeButton.trigger('cds-click');
expect(wrapper.emitted('close')).toBeTruthy();
});

test('shows missing fields warning and amber variant when required fields are missing', async () => {
const incompleteCitizenWrapper = await mount(CitizenSummaryViewer, {
props: {
citizen: incompleteCitizen,
},
global: {
plugins: [Cuida],
stubs: {
...stubs,
CdsText: false,
},
},
});

const missingFieldsAlert = incompleteCitizenWrapper.find('[data-testid="missing-fields-alert"]');

expect(incompleteCitizenWrapper.vm.hasMissingFields).toBeTruthy();
expect(incompleteCitizenWrapper.find('.box--amber').exists()).toBeTruthy();
expect(missingFieldsAlert.exists()).toBeTruthy();
expect(missingFieldsAlert.find('svg[aria-labelledby="warning-outline"]').exists()).toBeTruthy();
expect(missingFieldsAlert.text()).toContain('Cadastro incompleto');

incompleteCitizenWrapper.unmount();
});

test('should not show missing fields warning when hidden fields fields are missing', async () => {
const incompleteCitizenWrapper = await mount(CitizenSummaryViewer, {
props: {
citizen: incompleteCitizen,
hiddenFields: ['mother_name'],
},
global: {
plugins: [Cuida],
stubs: {
...stubs,
CdsText: false,
},
},
});

expect(incompleteCitizenWrapper.find('[data-testid="missing-fields-alert"]').exists()).toBeFalsy();
expect(incompleteCitizenWrapper.vm.hasMissingFields).toBeFalsy();
expect(incompleteCitizenWrapper.find('.box--amber').exists()).toBeFalsy();

incompleteCitizenWrapper.unmount();
});
});
92 changes: 86 additions & 6 deletions src/components/CitizenSummaryViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
<div
:class="{ 'summary--limit-width': !fluid }"
>
<CdsBox fluid>
<CdsBox
:variant="hasMissingFields ? 'amber' : 'gray'"
fluid
>
<CdsSpacer
v-if="!citizen"
>
Expand Down Expand Up @@ -52,19 +55,38 @@
</CdsBadge>
</CdsFlexbox>
<CdsFlexbox
v-if="!hideActions"
gap="3"
>
<CdsFlexbox
v-if="hasMissingFields"
align="center"
data-testid="missing-fields-alert"
gap="1"
>
<CdsIcon
height="24"
width="24"
name="warning-outline"
color="#EDA831"
/>
<CdsText
as="caption"
font-weight="semibold"
style="color: #EDA831"
>
Cadastro incompleto
</CdsText>
</CdsFlexbox>
<CdsIconButton
v-if="!hideEditButton"
v-if="!hideEditButton && !hideActions"
size="sm"
data-testid="edit-button"
tooltip-text="Editar"
icon="edit-outline"
@cds-click="emits('edit')"
/>
<CdsIconButton
v-if="!hideCloseButton"
v-if="!hideCloseButton && !hideActions"
size="sm"
data-testid="close-button"
tooltip-text="Fechar"
Expand All @@ -90,6 +112,7 @@

<script setup lang="ts">
import { watch, ref, onMounted, toRef } from 'vue';
import { isEmpty, isNil, isObject, isString } from 'lodash';
// @ts-ignore
import { smartTitleCase } from '@sysvale/foundry';
import { Citizen as CitizenModel } from '@/models/Citizen';
Expand Down Expand Up @@ -118,16 +141,73 @@ const emits = defineEmits(['create', 'edit', 'close']);

const internalCitizen = ref<CitizenModel>();
const emptyStateImage = emptyState;
const hasMissingFields = ref(false);

onMounted(() => fillCitizen(props.citizen));
onMounted(() => {
checkMissingRequiredFields(props.citizen);
fillCitizen(props.citizen);
});

watch(toRef(props, 'citizen'), (newValue) => fillCitizen(newValue));
watch(toRef(props, 'citizen'), (newValue) => {
checkMissingRequiredFields(newValue);
fillCitizen(newValue);
});

function fillCitizen(citizenInfo: any | null) {
if (!citizenInfo) return;

internalCitizen.value = new CitizenModel(citizenInfo);
}

function checkMissingRequiredFields(value: Partial<Citizen> | null) {
if (!value) {
hasMissingFields.value = false;
return;
}

const citizen = value as Record<string, unknown>;
const genderValue = citizen['gender'] ?? citizen['_gender'];
const addressValue = citizen['address'] ?? citizen['_address'];
const addressSource = (addressValue as Record<string, unknown>) ?? {
street: citizen['street'],
number: citizen['number'],
neighborhood: citizen['neighborhood'],
city: citizen['city'],
uf: citizen['uf'],
};

const missingCpfAndCnsFields = ['cpf', 'cns'].some(
field => isEmptyValue(citizen[field]) && !props.hiddenFields.includes(field)
);

const missingCitizenFields = ['name', 'birth_date', 'mother_name', 'race'].some(
field => isEmptyValue(citizen[field]) && !props.hiddenFields.includes(field)
) || isEmptyValue(genderValue);

const missingAddressFields = ['street', 'number', 'neighborhood', 'city', 'uf'].some(
field => isEmptyValue(addressSource?.[field]) && !props.hiddenFields.includes(field)
);

hasMissingFields.value = missingCitizenFields || missingAddressFields || missingCpfAndCnsFields;
}

function isEmptyValue(value: unknown) {
if (isNil(value)) return true;
if (isString(value)) return value.trim() === '';
if (!isObject(value)) return false;

const obj = value as Record<string, unknown>;
if ('value' in obj) return isEmptyValue(obj.value);
if ('name' in obj) return isEmptyValue(obj.name);
if ('shortName' in obj) return isEmptyValue(obj.shortName);
if ('id' in obj) return isEmptyValue(obj.id);

return isEmpty(obj);
}

defineExpose({
hasMissingFields,
})
</script>

<style lang="scss" scoped>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ exports[`CitizenSummaryViewer > is rendered correctly 1`] = `
<!--v-if-->
</div>
<div data-v-76655835="" data-v-07590091="" class="flexbox">
<!--v-if-->
<cds-icon-button-stub data-v-07590091="" size="sm" icon="edit-outline" disabled="false" tooltiptext="Editar" feedbackonclick="false" feedbackicon="check-outline" variant="white" data-testid="edit-button"></cds-icon-button-stub>
<cds-icon-button-stub data-v-07590091="" size="sm" icon="x-outline" disabled="false" tooltiptext="Fechar" feedbackonclick="false" feedbackicon="check-outline" variant="white" data-testid="close-button"></cds-icon-button-stub>
</div>
Expand Down
41 changes: 23 additions & 18 deletions src/models/Address.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import ufs from '@/constants/ufs';
import type { Address as AddressType } from '@/types';

type City = {
id: string;
Expand All @@ -23,6 +22,12 @@ type Uf = {
shortName: string;
};

function resolveFancyField(field: string | undefined): string {
if (!field) return 'Não informado';

return field;
}

export class Address {
public number?: string;
public complement?: string;
Expand All @@ -31,7 +36,7 @@ export class Address {
private _neighborhood?: Neighborhood;
private _street?: Street;

constructor(args: AddressType) {
constructor(args: any) {
this.number = args.number;
this.complement = args.complement;
this.city = args.city;
Expand All @@ -46,9 +51,9 @@ export class Address {
this._city =
typeof city === 'string'
? {
id: city,
value: city,
}
id: city,
value: city,
}
: city;
}

Expand All @@ -58,9 +63,9 @@ export class Address {
this._neighborhood =
typeof neighborhood === 'string'
? {
id: neighborhood,
value: neighborhood,
}
id: neighborhood,
value: neighborhood,
}
: neighborhood;
}

Expand All @@ -70,9 +75,9 @@ export class Address {
this._street =
typeof street === 'string'
? {
id: street,
value: street,
}
id: street,
value: street,
}
: street;
}

Expand Down Expand Up @@ -116,15 +121,15 @@ export class Address {

get fancyAddress(): string {
if (!this.city || !this.uf) {
return `${this.street?.value},
${this.number},
${this.neighborhood?.value}`;
return `${resolveFancyField(this.street?.value)},
${resolveFancyField(this.number)},
${resolveFancyField(this.neighborhood?.value)}`;
}

return `${this.street?.value},
${this.number},
${this.neighborhood?.value},
${this.city.value} - ${this.uf.shortName}`;
return `${resolveFancyField(this.street?.value)},
${resolveFancyField(this.number)},
${resolveFancyField(this.neighborhood?.value)},
${resolveFancyField(this.city?.value)} - ${resolveFancyField(this.uf?.shortName)}`;
}

get asFormData() {
Expand Down
6 changes: 3 additions & 3 deletions src/models/Citizen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe('Citizen model', () => {
});

test('gender is resolved correctly with both types', () => {
const gender = genderFromType(citizenFixture.gender);
const gender = genderFromType(citizenFixture.gender as string);
const newCitizen = new Citizen({
...citizenFixture,
gender,
Expand All @@ -33,7 +33,7 @@ describe('Citizen model', () => {
});

test('personalInfo is resolved correctly', () => {
const fancyGender = genderFromType(citizenFixture.gender).name;
const fancyGender = genderFromType(citizenFixture.gender as string).name;
const fancyRace = raceByValue(citizenFixture.race).name;

const fieldsToHide = ['race'];
Expand Down Expand Up @@ -82,6 +82,6 @@ describe('Citizen model', () => {
test('fancyAddress is created correctly', () => {
const address = new Address(citizenFixture.address);

expect(citizen.fancyAddress).toEqual(address.fancyAddress);
expect(citizen.fancyAddress).toEqual(address?.fancyAddress);
});
});
Loading