Saiful Islam 2023-03-06 15:34:22 +06:00
parent 25f44b84df
commit f2ed2c6642
7 changed files with 157 additions and 6 deletions

View File

@ -7,8 +7,9 @@ import CopyButton from 'components/common/CopyButton';
export default function TrackingCodeForm({ values, onClose }) {
const ref = useRef();
const ref1 = useRef();
const { basePath } = useRouter();
const { name, shareId } = values;
const { name, shareId, websiteUuid } = values;
return (
<FormLayout>
@ -33,6 +34,30 @@ export default function TrackingCodeForm({ values, onClose }) {
</FormRow>
<FormButtons>
<CopyButton type="submit" variant="action" element={ref} />
</FormButtons>
<p>
<FormattedMessage
id="message.embedded-url"
defaultMessage="This is the public embedded code for {target}."
values={{ target: <b>{values.name}</b> }}
/>
</p>
<FormRow>
<textarea
ref={ref1}
rows={3}
cols={60}
spellCheck={false}
defaultValue={`<canvas id="unamiChart" style="height: 300px;" data-url="${document.location.origin}${basePath}" data-website="${websiteUuid}" data-share-id="${shareId}" data-reload="5" data-start-at="auto" data-end-at="auto" data-unit="hour" data-tz="Asia/Dhaka"></canvas>
<script src="${document.location.origin}/embedded.js"></script>
`}
readOnly
/>
</FormRow>
<FormButtons>
<CopyButton type="submit" variant="action" element={ref1} />
</FormButtons>
<FormButtons>
<Button onClick={onClose}>
<FormattedMessage id="label.cancel" defaultMessage="Cancel" />
</Button>

100
embedded/index.js Normal file
View File

@ -0,0 +1,100 @@
/* eslint-disable */
if (typeof jQuery === 'undefined') {
const script = document.createElement('script');
script.src = '//code.jquery.com/jquery.js';
document.head.appendChild(script);
}
if (typeof Chart === 'undefined') {
const script = document.createElement('script');
script.src = '//cdn.jsdelivr.net/npm/chart.js';
script.onload = function () {
window.unamiChart = false;
loadUnamiStat();
};
document.head.appendChild(script);
}
function loadUnamiStat() {
var startAt = $('#unamiChart').data('start-at');
var endAt = $('#unamiChart').data('end-at');
var unit = $('#unamiChart').data('unit');
var tz = $('#unamiChart').data('tz');
var shareId = $('#unamiChart').data('share-id');
var reload = Number($('#unamiChart').data('reload'));
if ($('#unamiChart').length) {
loadUnamiDataAjax(startAt, endAt, unit, tz, shareId);
if (reload > 0) {
setInterval(function () {
loadUnamiDataAjax(startAt, endAt, unit, tz, shareId);
}, 1000 * reload);
}
} else {
alert('canvas#unamiChart does not exist!');
}
}
function loadUnamiDataAjax(startAt, endAt, unit, tz, shareId) {
if (startAt == 'auto') {
startAt = new Date() * 1 - 24 * 3600 * 1000;
}
if (endAt == 'auto') {
endAt = new Date() * 1;
}
var website = $('#unamiChart').data('website');
var apiUrl = $('#unamiChart').data('url');
var chartOptions = {
responsive: true,
maintainAspectRatio: false,
legend: { position: 'top' },
scales: { x: { stacked: true }, y: { stacked: true } },
};
var url = `${apiUrl}/api/websites/${website}/pageviews?start_at=${startAt}&end_at=${endAt}&unit=${unit}&tz=${tz}&shareId=${shareId}`;
$.ajax({
method: 'GET',
url: url,
success: function (data) {
var labels = data.pageviews.map(p => {
var date = new Date(p.t);
var hours = date.getHours();
return `${hours === 0 ? 12 : hours > 12 ? hours - 12 : hours}${hours < 12 ? 'AM' : 'PM'}`;
});
var pageviewsData = data.pageviews.map(p => p.y);
var sessionsData = data.sessions.map(s => s.y);
var chartData = {
labels: labels,
datasets: [
{
label: 'Unique visitors',
data: sessionsData,
backgroundColor: 'rgba(38, 128, 235, 0.6)',
borderColor: 'rgba(38, 128, 235, 0.7)',
},
{
label: 'Page views',
data: pageviewsData,
backgroundColor: 'rgba(38, 128, 235, 0.4)',
borderColor: 'rgba(38, 128, 235, 0.5)',
},
],
};
if (!window.unamiChart) {
var ctx = document.getElementById('unamiChart').getContext('2d');
window.unamiChart = new Chart(ctx, {
type: 'bar',
data: chartData,
options: chartOptions,
});
} else {
window.unamiChart.data = chartData;
window.unamiChart.update('none');
}
},
error: function (error) {
console.log(error);
},
});
}

View File

@ -94,6 +94,7 @@
"message.track-stats": "To track stats for {target}, place the following code in the {head} section of your website.",
"message.type-delete": "Type {delete} in the box below to confirm.",
"message.type-reset": "Type {reset} in the box below to confirm.",
"message.embedded-url": "This is the public embedded code for {target}.",
"metrics.actions": "Actions",
"metrics.average-visit-time": "Average visit time",
"metrics.bounce-rate": "Bounce rate",

View File

@ -2,6 +2,7 @@ import { createMiddleware, unauthorized, badRequest, serverError } from 'next-ba
import cors from 'cors';
import { getSession } from './session';
import { getAuthToken, getShareToken } from './auth';
import { getWebsite } from 'queries';
export const useCors = createMiddleware(
cors({
@ -32,7 +33,14 @@ export const useSession = createMiddleware(async (req, res, next) => {
export const useAuth = createMiddleware(async (req, res, next) => {
const token = await getAuthToken(req);
const shareToken = await getShareToken(req);
let shareToken = await getShareToken(req);
// create share token if shareId and website exist
if (req?.query?.shareId) {
const website = await getWebsite({ shareId: req?.query?.shareId });
if (website) {
shareToken = { id: website?.websiteUuid };
}
}
if (!token && !shareToken) {
return unauthorized(res);

View File

@ -11,14 +11,15 @@
},
"scripts": {
"dev": "next dev",
"build": "npm-run-all build-db check-db build-tracker build-geo build-app",
"build": "npm-run-all build-db check-db build-tracker build-embedded build-geo build-app",
"start": "next start",
"build-docker": "npm-run-all build-db build-tracker build-geo build-app",
"build-docker": "npm-run-all build-db build-tracker build-embedded build-geo build-app",
"start-docker": "npm-run-all check-db update-tracker start-server",
"start-env": "node scripts/start-env.js",
"start-server": "node server.js",
"build-app": "next build",
"build-tracker": "rollup -c rollup.tracker.config.js",
"build-embedded": "rollup -c rollup.embedded.config.js",
"build-db": "npm-run-all copy-db-files build-db-client",
"build-lang": "npm-run-all format-lang compile-lang download-country-names download-language-names",
"build-geo": "node scripts/build-geo.js",

View File

@ -4,6 +4,7 @@ import { ok, badRequest, methodNotAllowed, unauthorized } from 'next-basics';
import { allowQuery } from 'lib/auth';
import { useAuth, useCors } from 'lib/middleware';
import { TYPE_WEBSITE } from 'lib/constants';
import { getDateArray } from 'lib/date';
const unitTypes = ['year', 'month', 'hour', 'day'];
@ -12,7 +13,7 @@ export default async (req, res) => {
await useAuth(req, res);
if (req.method === 'GET') {
if (!(await allowQuery(req, TYPE_WEBSITE))) {
if (!(await allowQuery(req, TYPE_WEBSITE, true))) {
return unauthorized(res);
}
@ -69,7 +70,10 @@ export default async (req, res) => {
}),
]);
return ok(res, { pageviews, sessions });
let _pageviews = getDateArray(pageviews, startDate, endDate, unit);
let _sessions = getDateArray(sessions, startDate, endDate, unit);
return ok(res, { pageviews: _pageviews, sessions: _sessions });
}
return methodNotAllowed(res);

12
rollup.embedded.config.js Normal file
View File

@ -0,0 +1,12 @@
import 'dotenv/config';
import buble from '@rollup/plugin-buble';
import { terser } from 'rollup-plugin-terser';
export default {
input: 'embedded/index.js',
output: {
file: 'public/embedded.js',
format: 'iife',
},
plugins: [buble({ objectAssign: true }), terser({ compress: { evaluate: false } })],
};