Skip to content

Host a Zendesk form for all our products#175

Merged
davinotdavid merged 23 commits intomainfrom
host-zendesk-form
Jul 9, 2025
Merged

Host a Zendesk form for all our products#175
davinotdavid merged 23 commits intomainfrom
host-zendesk-form

Conversation

@davinotdavid
Copy link
Contributor

@davinotdavid davinotdavid commented Jun 24, 2025

Description of the Change

[Backend changes]

[Frontend changes]

  • Added a new ContactSupportVue that uses the services-ui form components for visual consistency (see image below). Please note that the UI itself will probably evolve / change very soon so please focus on the functionality itself for the time being.

image

Important

The original Zendesk form is configured with custom_fields that require specific field IDs to work. The Vue form values (the select values) and the Python env vars related to those need to be in sync with whatever is configured in Zendesk. In other words, changing Zendesk's custom_fields will break the form if they don't get updated in the code. More info here.

Benefits

  • Be able to host the form in our domains instead of redirecting the user to Zendesk's while maintaining similar functionality (potentially even improving on current functionality as we might be a little more flexible on fields).

Applicable Issues

#173

@davinotdavid davinotdavid changed the title [WIP] Host a Zendesk form for all our products Host a Zendesk form for all our products Jun 26, 2025
@davinotdavid davinotdavid marked this pull request as ready for review June 26, 2025 22:27
@davinotdavid davinotdavid requested a review from malini June 26, 2025 22:28
@davinotdavid
Copy link
Contributor Author

For testing, one will need to have the following .env vars:

# Zendesk (contact support form)
ZENDESK_SUBDOMAIN=
ZENDESK_USER_EMAIL=
ZENDESK_API_TOKEN=<Zendesk API Key for accounts.tb.pro in 1Password>

# Zendesk (contact support form) custom fields
ZENDESK_PRODUCT_FIELD_ID=
ZENDESK_TYPE_OF_REQUEST_FIELD_ID=

We currently don't have a Zendesk Sandbox account so beware that creating tickets through this form while running this locally, actually creates tickets that the support team sees. Please label them accordingly. Also, the credentials needed for the .env are therefore sensitive.

Copy link
Member

@MelissaAutumn MelissaAutumn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we dynamically retrieve the product and type fields on the backend and provide them to the frontend?

And thanks for getting this up. Lots of good work, just some feedback.

.env.example Outdated

# Zendesk (contact support form) custom fields
ZENDESK_PRODUCT_FIELD_ID=
ZENDESK_TYPE_OF_REQUEST_FIELD_ID=
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we dynamically pull these on the backend and add them to window._site on page render?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially, there's an endpoint for it in Zendesk API but the API key that we have at the moment doesn't have permissions to make the request.

I can see the "pro" of getting them dynamically by allowing the changes in the Zendesk dashboard to be applied automagically in the form without code intervention but the "con" of it is always having an extra request when loading. I wonder if those fields are going to be changed a lot or not 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you follow up with @malini tomorrow if you haven't already gotten a new token with read permissions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now getting the fields dynamically! Currently, there is only a specific user that have access to that endpoint so I will DM you the credentials to be used for testing this. Thanks! 🙇

{ label: 'Thunderbird Assist', value: 'thunderbird_assist' },
{ label: 'Thunderbird Appointment', value: 'thunderbird_appointment' },
{ label: 'Thunderbird Send', value: 'thunderbird_send' }
];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these will need to just be the product names, we might need to drop Assist for now (I'm not sure if we're launching with that) and add Thundermail.

Copy link
Contributor Author

@davinotdavid davinotdavid Jun 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I thought these need to be the exact values as expected from the Zendesk form configs so I got those from here: #173 (comment) (happy to change them here and sync with someone who has Zendesk access to change there too, of course!)

{ label: 'Provide Feedback or Request Features', value: 'provide_feedback_or_request_features' },
{ label: 'Payments & Billing', value: 'payments___billing' },
{ label: 'Not listed', value: 'not_listed' }
];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't getting passed into zendesk correctly.

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks correct to me 🤔 Wasn't this submitted as "Not listed" from the form? When I create tickets with other request types, it creates them correctly as well like:

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe I chose provide_feedback_or_request_features

{
"id": settings.ZENDESK_TYPE_OF_REQUEST_FIELD_ID,
"value": ticket_fields.get("ticket_type")
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might need to double check this as it's not being passed through.

Copy link
Member

@MelissaAutumn MelissaAutumn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor things, looking good!

return JsonResponse({
'success': True,
'ticket_fields': fields_with_options
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we control the returb structure here, we should return the data in a way that the frontend can just use without any post processing.

I think making field_with_options a dict keyed by title would work

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah that makes sense, much better indeed! Changed in this commit.


const data = await response.json();

if (data.success && data.ticket_fields) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to just pick the field type out of the return value and set it without any looping.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that makes sense! Thanks! Changed in this commit.

const data = await response.json();

if (!data.success) {
errorText.value = 'Failed to submit contact form';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we get an error from the server? if so please show the error on the frontend

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do indeed, changed here, thanks!

if not result['success']:
return JsonResponse({
'success': False,
'error': result.get('error', 'Failed to fetch ticket fields')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the backend can you wrap text with _( ... ). Eventually we'll have localization, and _() is just an easy way to extract and replace strings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL! Thanks, changed here and will keep that in mind for the next tasks on accounts 🙇

zendesk_api_response = zendesk_client.upload_file(uploaded_file)

if not zendesk_api_response['success']:
return JsonResponse(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing l10n _( ... ). For strings with variables you'll want to switch to .format iirc.

except Exception as e:
return JsonResponse({
'success': False,
'error': f'Failed to upload file {uploaded_file.name}: {str(e)}'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Member

@MelissaAutumn MelissaAutumn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, I'll add the secrets post merge.


// Set Product field options and ID
if (fields['Product']) {
const productField = fields['Product'];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably constify these titles. But it's a non-blocking issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, thanks! Added the constants to the same file for now but can move it to a move designated space in the future

@davinotdavid davinotdavid merged commit 532f663 into main Jul 9, 2025
2 checks passed
@davinotdavid davinotdavid deleted the host-zendesk-form branch July 9, 2025 18:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants