Track page visits

Hello community! :raising_hand_man:
I am building a talent platform where companies have access to specific technical talent and can hire from.
Companies need to make an account to get access. What I would now like to build is some sort of tracker, where I can see which (logged-in) user visited which page/profile and what time. Is there any smart and easy way to do that?

Any help is appreciated :pray:

Michael

What I have done in the past is use Make to set up a webhook that I can call from custom code that runs on every page, passes in the page slug and info about the logged-in user, and then in the webhook handler, write a record into a table in Airtable.

If youā€™re looking for a no-code solution, another way to go would be to find an analytics service that can log and report on custom page-specific info, and integrate that. But I donā€™t know of a specific one to recommend for that. (If you find one, please post here!)

Thanks for the input @dcoletta. Make seems like a potential option.
I will look into google analytics to see if there is an option to use that for now.

In case anyone has experience and has done something similar, Iā€™d be happy to get input!

Thanks, Michael

@dcoletta I would desperately like to implement your initial suggestion, however, is it realistically implementable by your average no-coder, such as myself? Can you reference any resource to help with the set? (Iā€™m already a make user)

Iā€™m really missing the ā€˜Last Seen Dateā€™ feature being on the professional plan, especially now with the price hike. Itā€™s the single feature I would benefit from the business plan, everything else would be pure excess - I just canā€™t justify the extra expense.

1 Like

On the Softr side, add a field called ā€œLast Seen Timeā€ to your Users table.

On the Make side, create a scenario with a webhook module, a webhook response module, an Airtable ā€œSearch Recordsā€ module, and an Airtable ā€œUpdate Recordā€ module. Wire up the modules so that the search records module looks up a record in your Users table using the email parameter passed into the webhook, and the Airtable update record module updates the Last Seen Time field of the record with the value formatDate(now). Schedule the scenario to run as data comes in.

Back on the Softr app side, add the following JavaScript to your siteā€™s custom code in the Code inside footer section, replacing the value of the webhookUrl variable with the webhook URL of the webhook module you created:

<script>
  const webhookUrl = "your-webhook-url-here";
  async function postLastSeenTime(url, data) {
      const response = await fetch(url, {
        method: 'POST', 
        mode: 'cors', 
        cache: 'no-cache', 
        credentials: 'same-origin',
        headers: {
          'Content-Type': 'application/json'
        },
        redirect: 'follow',
        referrerPolicy: 'no-referrer',
        body: JSON.stringify(data)
      });
      return response;
  }
  postLastSeenTime(webhookUrl, {
    email: window.logged_in_user.softr_user_email
  }).then((data) => {
      console.log(data);
  });
</script>

This worked for me. But I didnā€™t spend any time on error handling. For example, as coded it assumes that all users will be logged in, but depending on your app, you may need to test to make sure the user is logged in before sending the webhook.

To answer your original questionā€¦ this might be more than you want to take on as a no-coder. The analytics approach might be more straightforward!

3 Likes

Thank you so much! Iā€™ll have a go at implementing this as soon as I have a spare window and Iā€™ll report back - I really appreciate this :+1:

Thanks for the script, working well :fire: If someone knows how to manage the error handling of not logged in I will update my webhook ahah.

@lea you would need to wrap the code below with if(window.logged_in_user) { }

1 Like

Thanks :fire:

Thank you so much @dcoletta, this works a dream! And thank you @artur too for adding the extra functionality. Youā€™re both stars :star::star:

1 Like

Hi all!

Nice solution @dcoletta.

I would suggest a small improvement: it is possible to replace window.logged_in_user.softr_user_email with window.logged_in_user['airtable_record_id'] and therefore not having to use a search module within Make.

Also, this way of monitoring may be extremely action consuming on Make side, depending on the number of userā€¦ I am wondering if there would be another way.

There is also the possibility to use the ā€œWhen webhook receivedā€ trigger directly within Airtable that would be even easier to implement. It would need the date to be passed in the body.

The code would then be something like:

<script>
  const webhookUrl = "airtable-webhook-url-here";
  async function postLastSeenTime(url, data) {
      const response = await fetch(url, {
        method: 'POST', 
        mode: 'cors', 
        cache: 'no-cache', 
        credentials: 'same-origin',
        headers: {
          'Content-Type': 'application/json'
        },
        redirect: 'follow',
        referrerPolicy: 'no-referrer',
        body: JSON.stringify(data)
      });
      return response;
  }
if(window.logged_in_user) { 
  postLastSeenTime(webhookUrl, {
    recordId: window.logged_in_user['airtable_record_id'],
    date: Date.now();
  }).then((data) => {
      console.log(data);
  });
}
</script>

The date will probably need to be formatted somehow though.

It would still consume Airtable automations.
Would anyone have an idea on how to get the data only once per day/per session? Appart from having a server with a separate db to store all activity for one day and then updating Airtable?

1 Like

I asked chatgpt and they said to use cookies. I tested it and it works. Hereā€™s the code

const webhookUrl = "airtable-webhook-url-here";

// Check if the code has already been executed today
const lastExecutionTimestamp = localStorage.getItem('lastExecutionTimestamp');
const today = new Date().toISOString().split('T')[0]; // Get the current date in YYYY-MM-DD format

if (window.logged_in_user && lastExecutionTimestamp !== today) {
  async function postLastSeenTime(url, data) {
    const response = await fetch(url, {
      method: 'POST',
      mode: 'cors',
      cache: 'no-cache',
      credentials: 'same-origin',
      headers: {
        'Content-Type': 'application/json'
      },
      redirect: 'follow',
      referrerPolicy: 'no-referrer',
      body: JSON.stringify(data)
    });
    return response;
  }

  postLastSeenTime(webhookUrl, {
    recordId: window.logged_in_user['airtable_record_id'],
    date: Date.now()
  }).then((data) => {
    console.log(data);
    // Store the current timestamp to indicate the code has been executed today
    localStorage.setItem('lastExecutionTimestamp', today);
  });
}

Iā€™m getting an error in the console (when using an Airtable Automation generated webhook):

[Error] Fetch API cannot load https://hooks.airtable.com/workflows/v1/genericWebhook/(webhook data here) due to access control checks.

What would I need to do to prevent the error please?

It appears that AirTableā€™s webhook server does not provide the necessary header to allow a CORS request, which is what is needed here. Aside from some CORS workarounds that arenā€™t ideal (either set up a proxy, or use a browser extension), a better alternative is to modify the suggestion from @jclay12345 to use a ā€œsimple CORS requestā€ which limits what can be in the header, and requires the body be encoded as it would be from a form (not JSON).

When using this on Firefox, there are still CORS-related errors reported in the browser console, but AirTable updates the record successfully.

<script>
const webhookUrl = "airtable-webhook-url-here";

// Check if the code has already been executed today
const lastExecutionTimestamp = localStorage.getItem('lastExecutionTimestamp');
const today = new Date().toISOString().split('T')[0]; // Get the current date in YYYY-MM-DD format

if (window.logged_in_user && lastExecutionTimestamp !== today) {
  async function postLastSeenTime(url, data) {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: new URLSearchParams(data).toString()
    });
    return response;
  }

  postLastSeenTime(webhookUrl, {
    recordId: window.logged_in_user['airtable_record_id'],
    date: today
  }).then((data) => {
    console.log(data);
    // Store the current timestamp to indicate the code has been executed today
    localStorage.setItem('lastExecutionTimestamp', today);
  });
}
</script>
2 Likes

Thank you. Just to add that I got around the issue with a third party service:

const webhookUrl = 'https://corsproxy.io/?' + encodeURIComponent('https:// Airtable web hook )