Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
Ilkka Korhonen authored and Ilkka Korhonen committed Jul 23, 2024
2 parents 2bfcc73 + 3956139 commit c77fd80
Show file tree
Hide file tree
Showing 16 changed files with 357 additions and 56 deletions.
5 changes: 4 additions & 1 deletion frontend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@

# Application server key for web push notifications.
# Set the same value as <VAPID_PUBLIC_KEY> in backend .env file
REACT_APP_SERVER_KEY=''
VITE_REACT_APP_SERVER_KEY=''

VITE_CLARITY_ID=''
VITE_GOOGLE_ANALYTICS_ID=''
27 changes: 27 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"notistack": "^3.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-microsoft-clarity": "^1.2.0",
"react-router-dom": "^5.3.3",
"styled-components": ">= 5",
"typescript": "^5.4.5",
Expand Down Expand Up @@ -77,6 +78,7 @@
"eslint-config-prettier": "^9.1.0",
"happy-dom": "^14.12.3",
"vite": "^5.3.1",
"vite-plugin-radar": "^0.9.6",
"vite-plugin-svgr": "^4.2.0",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.6.0",
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/analytics/AnalyticsEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Defines the common enum to use for event names across all analytics services
export enum AnalyticsEventEnum {
BOOKING,
BOOKING_END,
BOOKING_ADD_TIME,
BOOKING_DEDUCT_TIME,
BUILDING_SELECT
}

// Defines the common event names to use across all analytics services
export const analyticsEventMap: { [key in AnalyticsEventEnum]: string } = {
[AnalyticsEventEnum.BOOKING]: 'booking',
[AnalyticsEventEnum.BOOKING_END]: 'bookingEnd',
[AnalyticsEventEnum.BOOKING_ADD_TIME]: 'bookingAddTime',
[AnalyticsEventEnum.BOOKING_DEDUCT_TIME]: 'bookingDeductTime',
[AnalyticsEventEnum.BUILDING_SELECT]: 'buildingSelect'
};
17 changes: 17 additions & 0 deletions frontend/src/analytics/clarityService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { AnalyticsEventEnum, analyticsEventMap } from './AnalyticsEvent';

function isClarityEnabled(): boolean {
// @ts-ignore
if (!window.clarity) {
console.log('Clarity is not enabled!');
return false;
}
return true;
}

export function triggerClarityEvent(event: AnalyticsEventEnum) {
if (isClarityEnabled()) {
// @ts-ignore
window.clarity('event', analyticsEventMap[event]);
}
}
70 changes: 70 additions & 0 deletions frontend/src/analytics/googleAnalytics/googleAnalyticsEvents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { AnalyticsEventEnum } from '../AnalyticsEvent';
import { Booking, Building, Room } from '../../types';

// Basic interface for all google analytics events
export interface GoogleAnalyticsEvent {
eventType: AnalyticsEventEnum;
eventObject: object;
}

export class BookingEvent implements GoogleAnalyticsEvent {
eventType: AnalyticsEventEnum = AnalyticsEventEnum.BOOKING;
eventObject: object;

constructor(room: Room, duration: number) {
this.eventObject = {
roomName: room.name,
building: room.building,
duration: duration
};
}
}

export class BookingEndEvent implements GoogleAnalyticsEvent {
eventType: AnalyticsEventEnum = AnalyticsEventEnum.BOOKING_END;
eventObject: object;

constructor(room: Room) {
this.eventObject = {
roomName: room.name,
building: room.building
};
}
}

export class BookingAddTimeEvent implements GoogleAnalyticsEvent {
eventType: AnalyticsEventEnum = AnalyticsEventEnum.BOOKING_ADD_TIME;
eventObject: object;

constructor(booking: Booking) {
this.eventObject = {
roomName: booking.room.name,
building: booking.room.building,
bookingResources: booking.resourceStatus
};
}
}

export class BookingDeductTimeEvent implements GoogleAnalyticsEvent {
eventType: AnalyticsEventEnum = AnalyticsEventEnum.BOOKING_DEDUCT_TIME;
eventObject: object;

constructor(booking: Booking) {
this.eventObject = {
roomName: booking.room.name,
building: booking.room.building,
bookingResources: booking.resourceStatus
};
}
}

export class ChooseBuildingEvent implements GoogleAnalyticsEvent {
eventType: AnalyticsEventEnum = AnalyticsEventEnum.BUILDING_SELECT;
eventObject: object;

constructor(building: Building) {
this.eventObject = {
buildingName: building.name
};
}
}
22 changes: 22 additions & 0 deletions frontend/src/analytics/googleAnalytics/googleAnalyticsService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { analyticsEventMap } from '../AnalyticsEvent';
import { GoogleAnalyticsEvent } from './googleAnalyticsEvents';

function isGAEnabled(): boolean {
// @ts-ignore
if (!window.gtag) {
console.log('Google Analytics is not enabled!');
return false;
}
return true;
}

export function triggerGoogleAnalyticsEvent(event: GoogleAnalyticsEvent) {
if (isGAEnabled()) {
// @ts-ignore
window.gtag(
'event',
analyticsEventMap[event.eventType],
event.eventObject
);
}
}
14 changes: 14 additions & 0 deletions frontend/src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,24 @@ import LoginView from './login/LoginView';
import { CssBaseline, Divider, styled, ThemeProvider } from '@mui/material';
import { theme_2024 } from '../theme_2024';
import { UserSettingsProvider } from '../contexts/UserSettingsContext';
import { clarity } from 'react-microsoft-clarity';
import GETAROOM_ENV from '../util/getARoomEnv';

export const GarApp = styled(Divider)(() => ({}));

const App = () => {
if (GETAROOM_ENV().VITE_CLARITY_ID != null) {
// Start seeing data on the Clarity dashboard with your id
clarity.init(GETAROOM_ENV().VITE_CLARITY_ID as string);

clarity.consent();

// Check if Clarity has been initialized before calling its methods
if (clarity.hasStarted()) {
clarity.identify('USER_ID', { userProperty: 'value' });
}
}

return (
<ThemeProvider theme={theme_2024}>
<CssBaseline />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import NoRoomsCard from '../RoomCard/NoRoomsCard';
import { sortByFavoritedAndName } from '../../util/arrayUtils';
import { isAvailableFor } from '../util/AvailableTime';

const SKIP_CONFIRMATION = true;

const TimePickerButton = styled(ToggleButton)(() => ({
padding: '8px 16px',
backgroundColor: '#ce3b20',
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/components/BookingView/BookingView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ import BookingDrawer from '../BookingDrawer/BookingDrawer';
import { availableForMinutes } from '../util/AvailableTime';
import dayjs from 'dayjs';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { triggerClarityEvent } from '../../analytics/clarityService';
import { AnalyticsEventEnum } from '../../analytics/AnalyticsEvent';
import { triggerGoogleAnalyticsEvent } from '../../analytics/googleAnalytics/googleAnalyticsService';
import { BookingEvent } from '../../analytics/googleAnalytics/googleAnalyticsEvents';

const UPDATE_FREQUENCY = 30000;
const GET_RESERVED = true;
Expand Down Expand Up @@ -209,6 +213,9 @@ function BookingView(props: BookingViewProps) {
createSuccessNotification('Booking was successful');
setBookingLoading('false');
document.getElementById('main-view-content')?.scrollTo(0, 0);

triggerGoogleAnalyticsEvent(new BookingEvent(room, duration));
triggerClarityEvent(AnalyticsEventEnum.BOOKING);
})
.catch(() => {
createErrorNotification('Could not create booking');
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/components/ChooseOfficeView/ChooseOfficeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { Building, Preferences } from '../../types';
import CenteredProgress from '../util/CenteredProgress';
import { getBuildingsWithPosition } from '../../services/buildingService';
import { useUserSettings } from '../../contexts/UserSettingsContext';
import { triggerGoogleAnalyticsEvent } from '../../analytics/googleAnalytics/googleAnalyticsService';
import { ChooseBuildingEvent } from '../../analytics/googleAnalytics/googleAnalyticsEvents';

type ChooseOfficeViewProps = {
buildings: Building[];
Expand Down Expand Up @@ -66,6 +68,9 @@ const ChooseOfficeView = (props: ChooseOfficeViewProps) => {
.then((savedPreferences) => {
setPreferences(savedPreferences);
createSuccessNotification('Chose building ' + buildingId);
triggerGoogleAnalyticsEvent(
new ChooseBuildingEvent(foundBuilding)
);
goToMainView();
})
.catch(() => {
Expand Down
Loading

0 comments on commit c77fd80

Please sign in to comment.