Ticket Feedback

Ticket Client Feedback Project

Preview


Ticket Feedback Preview

Description

Feedback Form is built with Next.js and other technologies to make it reusable for clients to report problems through tickets.

Features

  1. Anonymous Authentication on the Server
  2. Form Validation
  3. Query String Injection
  4. Post Message Injection
  5. New Ticket Creation
  6. Print Screen (Internal and External)

Technologies

  1. Next.js 14
  2. Javascript and Typescript
  3. React Hook Form
  4. Yup
  5. Tailwind CSS
  6. CSS
  7. HTML2Canvas
  8. Axios
  9. Cookies Next
  10. React Icons
  11. And many other Javascript libraries

Form Fields

  1. Some fields which are visible on the frontend and are required to be filled.
  2. Other than the required fields, there're two features one for URL and other is Screenshot which will automatically be passed to API behind the scenes. The screenshot is taken with HTML2Canvas library and URL is taken from browser window.
Required fields to be filled by the user
  1. First Name
  2. Last Name
  3. Description
  4. Phone Number
  5. Email
  6. Ticket Type (The first option will be auto-selected so you won't see any error if you forget to select any option)
  7. Ticket Impact (The first option will be auto-selected so you won't see any error if you forget to select any option)

IFrame integration in other projects

Step by step on how to add the ticket feedback iframe in other projects, all examples are from the PHPR ERP project.

Step 1: Create a "feedbackModal" view

Create a view that will contain the feedback modal.

  • First, you need to add the links for the feedback css and icons. Redefine css class if necessary.

    <link
          rel="stylesheet"
          href="https://ticket-feedback-form.phpr.link/assets/css/all-forms-iframe-only.css"
    />
    <link
            rel="icon"
            href="https://ticket-feedback-form.phpr.link/assets/images/favicon.ico"
            type="image/x-icon"
    />
    <link
            rel="apple-touch-icon"
            href="https://ticket-feedback-form.phpr.link/assets/images/favicon.ico"
    />
    
    {# Redefinition of position for closeBtn, because for some reason it was a bit off in PHPR. #}
    <style>
        #closeBtn {
            top: 12px;
            right: 12px;
        }
    </style>
  • Second, add a div to contain the iframe, then a div wrapper inside the container.

    <div id="iframeContainer" class="iframeContainer" hidden>
        <div id="frame-wrapper">
        </div>
    </div>

    Inside the wrapper, add the close button (anchor) before the iframe. Just after the close button, add the iframe.

    <div id="iframeContainer" class="iframeContainer" hidden>
        <div id="frame-wrapper">
            <a href="#" id="closeBtn" title="Close">&times;</a>
    
            <iframe
                src="https://ticket-feedback-form.phpr.link/{{ locale }}/feedback"
                frameborder="0"
                allowfullscreen
                id="frame1"
                loading="lazy"
                onload="sendInformationsToiFrame(this, '{{ app.user.person.firstName }}', '{{ app.user.person.lastName }}', '{{ app.user.person.defaultCoordinate.contactsEmail }}', '{{ app.user.person.defaultCoordinate.contactsTel }}')"
              >
              </iframe>
        </div>
    </div>
  • After the iframeContainer div is completed with the iframe, add the necessary JS to make everything work together.

    <script>
        var modalLink = document.querySelector(".open-modal");
        var modalContainer = document.getElementById("iframeContainer");
        var closeBtn = document.getElementById("closeBtn");
        var iframe = document.getElementById("frame");
    
        function openModal() {
            modalContainer.style.display = "flex";
        }
    
        function closeModal() {
            modalContainer.style.display = "none";
        }
    
        modalLink.addEventListener("click", function (event) {
            event.preventDefault();
            openModal();
        });
    
        closeBtn.addEventListener("click", function (event) {
            closeModal();
        });
    
        var iframeLinks = document.querySelectorAll(".iframe-link");
        iframeLinks.forEach(function (link) {
            link.style.display = "inline";
        });
    </script>
  • At the end of the file, add the JS sources.

    {# Ticket feedback JS #}
    {# Needs html2canvas for screenshots #}
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
    
    {# Should be PHPC CDN + fallback local #}
    <script src="https://ticket-feedback-form.phpr.link/assets/js/new/sendAllInformationsToiFrame.js"></script>

Complete example from PHPR (the view is made using Twig):

<link
        rel="stylesheet"
        href="https://ticket-feedback-form.phpr.link/assets/css/all-forms-iframe-only.css"
/>
<link
        rel="icon"
        href="https://ticket-feedback-form.phpr.link/assets/images/favicon.ico"
        type="image/x-icon"
/>
<link
        rel="apple-touch-icon"
        href="https://ticket-feedback-form.phpr.link/assets/images/favicon.ico"
/>

{# Redefinition of position for closeBtn, because for some reason it was a bit off in PHPR. #}
<style>
    #closeBtn {
        top: 12px;
        right: 12px;
    }
</style>

<div id="iframeContainer" class="iframeContainer" hidden>
    <div id="frame-wrapper">
        <a href="#" id="closeBtn" title="Close">&times;</a>

        {% if app.request.locale is defined %}
            {% set locale = app.request.locale %}
            {% if locale == 'fr_CA' %}
                {% set locale = 'fr' %}
            {% elseif locale == 'en_CA' %}
                {% set locale = 'en' %}
            {% else %}
                {% set locale = 'fr' %}
            {% endif %}
        {% else %}
            {% set locale = 'fr' %}
        {% endif %}

        {% if app.user.person.defaultCoordinate is not null %}
            <iframe
                    src="https://ticket-feedback-form.phpr.link/{{ locale }}/feedback"
                    frameborder="0"
                    allowfullscreen
                    id="frame1"
                    loading="lazy"
                    onload="sendInformationsToiFrame(this, '{{ app.user.person.firstName }}', '{{ app.user.person.lastName }}', '{{ app.user.person.defaultCoordinate.contactsEmail }}', '{{ app.user.person.defaultCoordinate.contactsTel }}')"
            >
            </iframe>
        {% elseif app.user.username == 'admin' or app.user.username == 'phpc' %}
            <iframe
                    id="frame1"
                    src="https://ticket-feedback-form.phpr.link/{{ locale }}/feedback"
                    loading="lazy"
                    frameborder="0"
                    allowfullscreen
                    onload="sendInformationsToiFrame(this, 'Mr', 'Administrator', 'support@phpcreation.com', '0000-000-000')"
            >
            </iframe>
        {% else %}
            <iframe
                    id="frame1"
                    src="https://ticket-feedback-form.phpr.link/{{ locale }}/feedback"
                    loading="lazy"
                    frameborder="0"
                    allowfullscreen
                    onload="sendInformationsToiFrame(this)"
            >
            </iframe>
        {% endif %}
    </div>
</div>

<script>
    var modalLink = document.querySelector(".open-modal");
    var modalContainer = document.getElementById("iframeContainer");
    var closeBtn = document.getElementById("closeBtn");
    var iframe = document.getElementById("frame");

    function openModal() {
        modalContainer.style.display = "flex";
    }

    function closeModal() {
        modalContainer.style.display = "none";
    }

    modalLink.addEventListener("click", function (event) {
        event.preventDefault();
        openModal();
    });

    closeBtn.addEventListener("click", function (event) {
        closeModal();
    });

    var iframeLinks = document.querySelectorAll(".iframe-link");
    iframeLinks.forEach(function (link) {
        link.style.display = "inline";
    });
</script>

{# Ticket feedback JS #}
{# Needs html2canvas for screenshots #}
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>

{# Should be PHPC CDN + fallback local #}
<script src="https://ticket-feedback-form.phpr.link/assets/js/new/sendAllInformationsToiFrame.js"></script>

Add the "Report a problem" link

Example of a report a problem link from PHPR:

<a href="https://ticket-feedback-form.phpr.link/fr/feedback" class="iframe-link open-modal">{{ 'signalProblem'|trans }} <span class="glyphicons glyphicons-bug"></span></a>

Add the link to the feedback in the href as a fallback if the user has JS disabled for some reason.

Final step

Test the integration. The feedback should work correctly, redefine some css if necessary.

Post Message:

1. If you want to send basic data to the ticket you can use the JS function and send it through onload.
Use this script tag:
<script src="https://dev.ticket-feedback-form.phpr.link/assets/js/new/sendInformationsToiFrame.js"></script>
How to send data:
<iframe
    id="frame"
    src="https://dev.ticket-feedback-form.phpr.link/feedback"
    frameborder="0"
    allowfullscreen
    onload="sendInformationsToiFrame(this, 'Hadi', 'Haider', 'support@phpcreation.com', '(450) 305-6253', 'This is a dev note', 'Test description from demo!', true, 'This is a reproduction note')"
></iframe>

sendInformationsToiFrame function will take these inputs:

  1. Frame (this)
  2. First Name
  3. Last Name
  4. Email Address
  5. Phone Number
  6. Dev Note
  7. Client Demand
  8. Client Urgency
  9. Reproduction Note
2. Send a Screenshot of the page only.
Use this script tag:
<script src="https://dev.ticket-feedback-form.phpr.link/assets/js/new/sendScreenShotToiFrame.js"></script>
How to send data:
<iframe
    id="frame"
    src="https://dev.ticket-feedback-form.phpr.link/feedback"
    frameborder="0"
    allowfullscreen
    onload="sendScreenShotToiFrame(this)"
></iframe>
3. Send a Form Urgency Status of the page only.
Use this script tag:
<script src="https://dev.ticket-feedback-form.phpr.link/assets/js/new/sendUrgencyStatusToiFrame.js"></script>
How to send data:
<iframe
    id="frame"
    src="https://dev.ticket-feedback-form.phpr.link/feedback"
    frameborder="0"
    allowfullscreen
    onload="sendUrgencyStatusToiFrame(this, true)"
   >
</iframe>

Query Params:

There are a few query parameters used to fill the input fields directly.

  1. First Name -> https://dev.ticket-feedback-form.phpr.link/feedback?firstName=Simon
  2. Last Name -> https://dev.ticket-feedback-form.phpr.link/feedback?lastName=Tremblay
  3. Email -> https://dev.ticket-feedback-form.phpr.link/feedback?email=phpc@phpcreation.com
  4. Phone -> https://dev.ticket-feedback-form.phpr.link/feedback?phone=32189392011
  5. Description -> https://dev.ticket-feedback-form.phpr.link/en/feedback?clientDemand=Test%20Description%20from%20Demo
  6. Urgency -> https://dev.ticket-feedback-form.phpr.link/feedback?urgency=true
  7. URL -> https://dev.ticket-feedback-form.phpr.link/en/feedback?url=https://phpcreation.com
Send all parameters at once by separating them with &
https://dev.ticket-feedback-form.phpr.link/feedback?firstName=Simon&lastName=Tremblay&email=phpc@phpcreation.com&phone=32189392011&clientDemand=Test%20Description%20from%20Demo&urgency=true&url=https://phpcreation.com

What is the purpose of Keep contact details?

This switch button will be true by default however, you can update this. The purpose of this option is to make it easy for user to use the form again. The basic details will get saved into browser's localstorage and will be refetched again when ticket feedback is used. You can update the fields if the fields are already filled.

How to submit form?

Fill out the form details and click on Send button or press Enter key on your keyboard.

If don't fill out any of these fields and try to submit the form you will see these errors.


Ticket Feedback Preview

Emergency:

Click on this Alert button on the header.


Ticket Feedback Preview

This button will turn into Red and other fields too. The button text will change into Emergency and will have Red color.


Ticket Feedback Preview

Confirmation Page:


Ticket Feedback Preview
Features:
  1. Create New Ticket by clicking on the button Create New Ticket
  2. Preview Ticket Followup page

Followup Page:


Ticket Feedback Preview
Features:
  1. Close Ticket
  2. Create New Ticket
  3. Add a QA Note for Admins
  4. If you're an admin you can view this ticket on Ticket CRUD Project

Environments:

dev: Corresponds to development environment and GitHub CICD branch is main
staging: Corresponds to staging environment and GitHub CICD branch is staging
prod: Corresponds to production environment and GitHub CICD branch is production

References:

Dev link : https://dev.ticket-feedback-form.phpr.link/ (opens in a new tab)
Github URL : https://github.com/PHPCreation/ticket-client-feedback-react-nextjs (opens in a new tab)