diff --git a/app/Http/Requests/Admin/Settings/BaseSettingsFormRequest.php b/app/Http/Requests/Admin/Settings/BaseSettingsFormRequest.php index 0b02561dd..777761b67 100644 --- a/app/Http/Requests/Admin/Settings/BaseSettingsFormRequest.php +++ b/app/Http/Requests/Admin/Settings/BaseSettingsFormRequest.php @@ -19,6 +19,7 @@ class BaseSettingsFormRequest extends AdminFormRequest 'app:name' => 'required|string|max:255', 'pterodactyl:auth:2fa_required' => 'required|integer|in:0,1,2', 'app:locale' => ['required', 'string', Rule::in(array_keys($this->getAvailableLanguages()))], + 'app:analytics' => 'nullable|string', ]; } @@ -31,6 +32,7 @@ class BaseSettingsFormRequest extends AdminFormRequest 'app:name' => 'Company Name', 'pterodactyl:auth:2fa_required' => 'Require 2-Factor Authentication', 'app:locale' => 'Default Language', + 'app:analytics' => 'Google Analytics', ]; } } diff --git a/app/Http/ViewComposers/AssetComposer.php b/app/Http/ViewComposers/AssetComposer.php index 7e8f82dbc..6da825ad4 100644 --- a/app/Http/ViewComposers/AssetComposer.php +++ b/app/Http/ViewComposers/AssetComposer.php @@ -37,6 +37,7 @@ class AssetComposer 'enabled' => config('recaptcha.enabled', false), 'siteKey' => config('recaptcha.website_key') ?? '', ], + 'analytics' => config('app.analytics') ?? '', ]); } } diff --git a/app/Providers/SettingsServiceProvider.php b/app/Providers/SettingsServiceProvider.php index 8a1d4db21..abd88c04b 100644 --- a/app/Providers/SettingsServiceProvider.php +++ b/app/Providers/SettingsServiceProvider.php @@ -21,6 +21,7 @@ class SettingsServiceProvider extends ServiceProvider protected $keys = [ 'app:name', 'app:locale', + 'app:analytics', 'recaptcha:enabled', 'recaptcha:secret_key', 'recaptcha:website_key', diff --git a/package.json b/package.json index 99bcf0d37..3a81f98fa 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "path": "^0.12.7", "query-string": "^6.7.0", "react": "^16.13.1", + "react-ga": "^3.1.2", "react-dom": "npm:@hot-loader/react-dom", "react-fast-compare": "^3.2.0", "react-google-recaptcha": "^2.0.1", diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index dac7fd102..350387fac 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -1,4 +1,5 @@ -import * as React from 'react'; +import React, { useEffect } from 'react'; +import ReactGA from 'react-ga'; import { hot } from 'react-hot-loader/root'; import { BrowserRouter, Route, Switch } from 'react-router-dom'; import { StoreProvider } from 'easy-peasy'; @@ -48,6 +49,11 @@ const App = () => { store.getActions().settings.setSettings(SiteConfiguration!); } + useEffect(() => { + ReactGA.initialize(SiteConfiguration!.analytics); + ReactGA.pageview(location.pathname); + }, []); + return ( <> diff --git a/resources/scripts/routers/AuthenticationRouter.tsx b/resources/scripts/routers/AuthenticationRouter.tsx index a7c687eef..57d1422ca 100644 --- a/resources/scripts/routers/AuthenticationRouter.tsx +++ b/resources/scripts/routers/AuthenticationRouter.tsx @@ -1,4 +1,5 @@ -import React from 'react'; +import React, { useEffect } from 'react'; +import ReactGA from 'react-ga'; import { Route, RouteComponentProps, Switch } from 'react-router-dom'; import LoginContainer from '@/components/auth/LoginContainer'; import ForgotPasswordContainer from '@/components/auth/ForgotPasswordContainer'; @@ -6,17 +7,23 @@ import ResetPasswordContainer from '@/components/auth/ResetPasswordContainer'; import LoginCheckpointContainer from '@/components/auth/LoginCheckpointContainer'; import NotFound from '@/components/screens/NotFound'; -export default ({ location, history, match }: RouteComponentProps) => ( -
- - - - - - - - history.push('/auth/login')}/> - - -
-); +export default ({ location, history, match }: RouteComponentProps) => { + useEffect(() => { + ReactGA.pageview(location.pathname); + }, [ location.pathname ]); + + return ( +
+ + + + + + + + history.push('/auth/login')} /> + + +
+ ); +}; diff --git a/resources/scripts/routers/DashboardRouter.tsx b/resources/scripts/routers/DashboardRouter.tsx index 79ebbe4a1..7a895a7e4 100644 --- a/resources/scripts/routers/DashboardRouter.tsx +++ b/resources/scripts/routers/DashboardRouter.tsx @@ -1,4 +1,5 @@ -import * as React from 'react'; +import React, { useEffect } from 'react'; +import ReactGA from 'react-ga'; import { NavLink, Route, RouteComponentProps, Switch } from 'react-router-dom'; import AccountOverviewContainer from '@/components/dashboard/AccountOverviewContainer'; import NavigationBar from '@/components/NavigationBar'; @@ -8,24 +9,30 @@ import NotFound from '@/components/screens/NotFound'; import TransitionRouter from '@/TransitionRouter'; import SubNavigation from '@/components/elements/SubNavigation'; -export default ({ location }: RouteComponentProps) => ( - <> - - {location.pathname.startsWith('/account') && - -
- Settings - API Credentials -
-
- } - - - - - - - - - -); +export default ({ location }: RouteComponentProps) => { + useEffect(() => { + ReactGA.pageview(location.pathname); + }, [ location.pathname ]); + + return ( + <> + + {location.pathname.startsWith('/account') && + +
+ Settings + API Credentials +
+
+ } + + + + + + + + + + ); +}; diff --git a/resources/scripts/routers/ServerRouter.tsx b/resources/scripts/routers/ServerRouter.tsx index 9df270eaa..2e9ee9ed3 100644 --- a/resources/scripts/routers/ServerRouter.tsx +++ b/resources/scripts/routers/ServerRouter.tsx @@ -1,4 +1,5 @@ import React, { useEffect, useState } from 'react'; +import ReactGA from 'react-ga'; import { NavLink, Route, RouteComponentProps, Switch } from 'react-router-dom'; import NavigationBar from '@/components/NavigationBar'; import ServerConsole from '@/components/server/ServerConsole'; @@ -60,6 +61,10 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) }; }, [ match.params.id ]); + useEffect(() => { + ReactGA.pageview(location.pathname); + }, [ location.pathname ]); + return ( diff --git a/resources/scripts/state/settings.ts b/resources/scripts/state/settings.ts index 20dbbdc6e..3eb782d91 100644 --- a/resources/scripts/state/settings.ts +++ b/resources/scripts/state/settings.ts @@ -7,6 +7,7 @@ export interface SiteSettings { enabled: boolean; siteKey: string; }; + analytics: string; } export interface SettingsStore { diff --git a/resources/views/admin/settings/index.blade.php b/resources/views/admin/settings/index.blade.php index 489646dc9..5ccec0dfa 100644 --- a/resources/views/admin/settings/index.blade.php +++ b/resources/views/admin/settings/index.blade.php @@ -31,6 +31,13 @@

This is the name that is used throughout the panel and in emails sent to clients.

+
+ +
+ +

This is your Google Analytics Tracking ID, Ex. UA-123723645-2

+
+
diff --git a/yarn.lock b/yarn.lock index 62c1da6fb..f20fef049 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5569,6 +5569,11 @@ react-fast-compare@^3.2.0: resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== +react-ga@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/react-ga/-/react-ga-3.1.2.tgz#e13f211c51a2e5c401ea69cf094b9501fe3c51ce" + integrity sha512-OJrMqaHEHbodm+XsnjA6ISBEHTwvpFrxco65mctzl/v3CASMSLSyUkFqz9yYrPDKGBUfNQzKCjuMJwctjlWBbw== + react-google-recaptcha@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/react-google-recaptcha/-/react-google-recaptcha-2.0.1.tgz#3276b29659493f7ca2a5b7739f6c239293cdf1d8"