diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..5da1bca2e --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,8 @@ +# GitHub Copilot Instructions + +- Always include pt-BR translations for any new text added to the project. +- fazer.ai is always styled as-is, with a dot and lowercase letters. Never use Fazer.ai +- Always check if adding specs is necessary when modifying code. +- Evaluate if specs added are actually needed and not redundant. Specs should not be for documentation purposes only, they should cover expected behavior. +- Always evaluate if frontend changes are needed when modifying backend code, and vice versa. +- NEVER use `--` in `pnpm test -- `. Just do `pnpm test ` directly diff --git a/app/javascript/dashboard/i18n/locale/en/contact.json b/app/javascript/dashboard/i18n/locale/en/contact.json index 32b37de55..fda12de28 100644 --- a/app/javascript/dashboard/i18n/locale/en/contact.json +++ b/app/javascript/dashboard/i18n/locale/en/contact.json @@ -61,7 +61,13 @@ "EDIT_CONTACT": { "BUTTON_LABEL": "Edit Contact", "TITLE": "Edit contact", - "DESC": "Edit contact details" + "DESC": "Edit contact details", + "CONFIRM_DISCARD": { + "TITLE": "Discard changes?", + "MESSAGE": "You have unsaved changes. Are you sure you want to discard them?", + "YES": "Discard", + "NO": "Keep editing" + } }, "DELETE_CONTACT": { "BUTTON_LABEL": "Delete Contact", diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/contact.json b/app/javascript/dashboard/i18n/locale/pt_BR/contact.json index 57ac2ec00..0ef030782 100644 --- a/app/javascript/dashboard/i18n/locale/pt_BR/contact.json +++ b/app/javascript/dashboard/i18n/locale/pt_BR/contact.json @@ -61,7 +61,13 @@ "EDIT_CONTACT": { "BUTTON_LABEL": "Editar Contato", "TITLE": "Editar contato", - "DESC": "Alterar detalhes do contato" + "DESC": "Alterar detalhes do contato", + "CONFIRM_DISCARD": { + "TITLE": "Descartar alterações?", + "MESSAGE": "Você tem alterações não salvas. Tem certeza de que deseja descartá-las?", + "YES": "Descartar", + "NO": "Continuar editando" + } }, "DELETE_CONTACT": { "BUTTON_LABEL": "Excluir Contato", diff --git a/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactForm.vue b/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactForm.vue index 2ada95084..314ad66f0 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactForm.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactForm.vue @@ -64,6 +64,7 @@ export default { { key: 'github', prefixURL: 'https://github.com/' }, { key: 'tiktok', prefixURL: 'https://tiktok.com/@' }, ], + initialData: null, }; }, validations: { @@ -100,8 +101,27 @@ export default { } return ''; }, + hasUnsavedChanges() { + if (!this.initialData) return false; + const socialProfilesChanged = this.socialProfileKeys.some( + ({ key }) => + (this.socialProfileUserNames[key] || '') !== + (this.initialData.socialProfileUserNames[key] || '') + ); + return ( + this.name !== this.initialData.name || + this.email !== this.initialData.email || + this.phoneNumber !== this.initialData.phoneNumber || + this.companyName !== this.initialData.companyName || + this.description !== this.initialData.description || + this.city !== this.initialData.city || + (this.country?.id || '') !== (this.initialData.countryId || '') || + this.avatarFile !== null || + socialProfilesChanged + ); + }, setPhoneNumber() { - if (this.parsePhoneNumber && this.parsePhoneNumber.countryCallingCode) { + if (this.parsePhoneNumber?.countryCallingCode) { return this.phoneNumber; } if (this.phoneNumber === '' && this.activeDialCode !== '') { @@ -175,6 +195,16 @@ export default { github: socialProfiles.github || '', instagram: socialProfiles.instagram || '', }; + this.initialData = { + name: this.name, + email: this.email, + phoneNumber: this.phoneNumber, + companyName: this.companyName, + description: this.description, + city: this.city, + countryId: this.country.id, + socialProfileUserNames: { ...this.socialProfileUserNames }, + }; }, getContactObject() { if (this.country === null) { @@ -253,7 +283,7 @@ export default { }, async handleAvatarDelete() { try { - if (this.contact && this.contact.id) { + if (this.contact?.id) { await this.$store.dispatch('contacts/deleteAvatar', this.contact.id); useAlert(this.$t('CONTACT_FORM.DELETE_AVATAR.API.SUCCESS_MESSAGE')); } diff --git a/app/javascript/dashboard/routes/dashboard/conversation/contact/EditContact.vue b/app/javascript/dashboard/routes/dashboard/conversation/contact/EditContact.vue index 33a14fe65..62eebd59a 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/contact/EditContact.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/contact/EditContact.vue @@ -32,7 +32,15 @@ export default { }, methods: { - onCancel() { + async onClose() { + const hasChanges = this.$refs.contactForm?.hasUnsavedChanges; + if (hasChanges) { + const shouldDiscard = + await this.$refs.confirmDiscardDialog.showConfirmation(); + if (!shouldDiscard) { + return; + } + } this.$emit('cancel'); }, onSuccess() { @@ -52,7 +60,7 @@ export default {