Initial commit: Atomaste website
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-date', 'wp-element', 'wp-i18n'), 'version' => 'ad0c02ae7d9e6c646bff');
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) or die( 'you do not have access to this page!' );
|
||||
if ( ! class_exists( 'burst_dashboard_widget' ) ) {
|
||||
class burst_dashboard_widget {
|
||||
private static $_this;
|
||||
public $error_message = '';
|
||||
|
||||
function __construct() {
|
||||
if ( isset( self::$_this ) ) {
|
||||
wp_die(
|
||||
burst_sprintf(
|
||||
'%s is a singleton class and you cannot create a second instance.',
|
||||
get_class( $this )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
self::$_this = $this;
|
||||
add_action( 'wp_dashboard_setup', [ $this, 'add_burst_dashboard_widget' ] );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
|
||||
}
|
||||
|
||||
static function this() {
|
||||
return self::$_this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Add a dashboard widget
|
||||
*
|
||||
* @since 1.1
|
||||
*/
|
||||
public function add_burst_dashboard_widget() {
|
||||
if ( ! burst_user_can_view() ) {
|
||||
return;
|
||||
}
|
||||
wp_add_dashboard_widget(
|
||||
'dashboard_widget_burst',
|
||||
'Burst Statistics',
|
||||
[
|
||||
$this,
|
||||
'render_dashboard_widget',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function enqueue( $hook ) {
|
||||
|
||||
if ( $hook !== 'index.php' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! burst_user_can_view() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$js_data = burst_get_chunk_translations( 'dashboard-widget/build' );
|
||||
if ( empty( $js_data ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_style( 'wp-components' );
|
||||
$handle = 'burst-settings';
|
||||
wp_enqueue_script(
|
||||
$handle,
|
||||
plugins_url( 'build/' . $js_data['js_file'], __FILE__ ),
|
||||
$js_data['dependencies'],
|
||||
$js_data['version'],
|
||||
true
|
||||
);
|
||||
wp_enqueue_style(
|
||||
$handle,
|
||||
plugins_url( 'build/index.css', __FILE__ ),
|
||||
array(),
|
||||
$js_data['version']
|
||||
);
|
||||
wp_set_script_translations( $handle, 'burst-statistics' );
|
||||
wp_localize_script(
|
||||
$handle,
|
||||
'burst_settings',
|
||||
burst_localized_settings( $js_data )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Renders the dashboard widget
|
||||
*/
|
||||
public function render_dashboard_widget() {
|
||||
echo '<div id="burst-widget-root"></div>';
|
||||
}
|
||||
}
|
||||
} //class closure
|
||||
@@ -0,0 +1,195 @@
|
||||
import {useEffect, useState} from '@wordpress/element';
|
||||
import './DashboardWidget.scss';
|
||||
import {setLocaleData} from '@wordpress/i18n';
|
||||
import {
|
||||
getAvailableRangesWithKeys
|
||||
} from '../../../../settings/src/utils/formatting';
|
||||
import getLiveVisitors from '../../../../settings/src/api/getLiveVisitors';
|
||||
import getTodayData from '../../../../settings/src/api/getTodayData';
|
||||
import GridItem from '../../../../settings/src/components/common/GridItem';
|
||||
import Icon from '../../../../settings/src/utils/Icon';
|
||||
import {format} from 'date-fns';
|
||||
import {__} from '@wordpress/i18n';
|
||||
import {useQueries} from '@tanstack/react-query';
|
||||
import {
|
||||
getLocalStorage,
|
||||
setLocalStorage
|
||||
} from '../../../../settings/src/utils/api';
|
||||
|
||||
|
||||
/**
|
||||
* Lazy load everything to keep light weight.
|
||||
*/
|
||||
|
||||
function selectVisitorIcon( value ) {
|
||||
value = parseInt( value );
|
||||
if ( 100 < value ) {
|
||||
return 'visitors-crowd';
|
||||
} else if ( 10 < value ) {
|
||||
return 'visitors';
|
||||
} else {
|
||||
return 'visitor';
|
||||
}
|
||||
}
|
||||
|
||||
const DashboardWidget = () => {
|
||||
const [ range, setRange ] = useState( getLocalStorage( 'widget_date_range', 'last-7-days' ) );
|
||||
useEffect( () => {
|
||||
burst_settings.json_translations.forEach( ( translationsString ) => {
|
||||
let translations = JSON.parse( translationsString );
|
||||
let localeData = translations.locale_data['burst-statistics'] ||
|
||||
translations.locale_data.messages;
|
||||
localeData[''].domain = 'burst-statistics';
|
||||
setLocaleData( localeData, 'burst-statistics' );
|
||||
});
|
||||
}, []);
|
||||
|
||||
// This function would be triggered when the user selects a new range.
|
||||
const handleRangeSelect = ( rangeKey ) => {
|
||||
setLocalStorage( 'widget_date_range', rangeKey );
|
||||
setRange( rangeKey );
|
||||
};
|
||||
|
||||
// Get the display dates for the selected range.
|
||||
const availableRanges = getAvailableRangesWithKeys(
|
||||
[ 'today', 'yesterday', 'last-7-days', 'last-30-days', 'last-90-days' ]);
|
||||
|
||||
// Get the currently selected range object.
|
||||
const selectedRange = availableRanges[range] ?
|
||||
availableRanges[range].range() :
|
||||
availableRanges['last-7-days'].range();
|
||||
const startDate = format( selectedRange.startDate, 'yyyy-MM-dd' );
|
||||
const endDate = format( selectedRange.endDate, 'yyyy-MM-dd' );
|
||||
|
||||
const [ interval, setInterval ] = useState( 5000 );
|
||||
const placeholderData = {
|
||||
live: {
|
||||
title: __( 'Live', 'burst-statistics' ),
|
||||
icon: 'visitor'
|
||||
},
|
||||
today: {
|
||||
title: __( 'Total', 'burst-statistics' ),
|
||||
value: '-',
|
||||
icon: 'visitor'
|
||||
},
|
||||
mostViewed: {
|
||||
title: '-',
|
||||
value: '-'
|
||||
},
|
||||
pageviews: {
|
||||
title: '-',
|
||||
value: '-'
|
||||
},
|
||||
referrer: {
|
||||
title: '-',
|
||||
value: '-'
|
||||
},
|
||||
timeOnPage: {
|
||||
title: '-',
|
||||
value: '-'
|
||||
}
|
||||
};
|
||||
const queries = useQueries({
|
||||
queries: [
|
||||
{
|
||||
queryKey: [ 'live-visitors' ],
|
||||
queryFn: getLiveVisitors,
|
||||
refetchInterval: interval,
|
||||
placeholderData: '-',
|
||||
onError: () => {
|
||||
setInterval( 0 );
|
||||
}
|
||||
},
|
||||
{
|
||||
queryKey: [ range, startDate, endDate ],
|
||||
queryFn: () => getTodayData({startDate, endDate}),
|
||||
refetchInterval: interval * 2,
|
||||
placeholderData: placeholderData,
|
||||
onError: () => {
|
||||
setInterval( 0 );
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
// Your existing code
|
||||
const live = queries[0].data;
|
||||
let data = queries[1].data;
|
||||
if ( queries.some( ( query ) => query.isError ) ) {
|
||||
data = placeholderData;
|
||||
}
|
||||
let liveIcon = selectVisitorIcon( live ? live : 0 );
|
||||
let todayIcon = 'loading';
|
||||
if ( data && data.today ) {
|
||||
todayIcon = selectVisitorIcon( data.today.value ? data.today.value : 0 );
|
||||
}
|
||||
return (
|
||||
<GridItem
|
||||
className={'border-to-border burst-today'}
|
||||
title={__( 'Today', 'burst-statistics' )}
|
||||
controls={<>{queries[0].isFetching ?
|
||||
<Icon name={'loading'}/> :
|
||||
null}</>}
|
||||
footer={
|
||||
<>
|
||||
<a className={'burst-button burst-button--secondary'}
|
||||
href={burst_settings.dashboard_url + '#statistics'}>{__(
|
||||
'View all statistics', 'burst-statistics' )}</a>
|
||||
<select onChange={( e ) => handleRangeSelect( e.target.value )}
|
||||
value={range}>
|
||||
{Object.keys( availableRanges ).map( ( key ) => (
|
||||
<option key={key} value={key}>
|
||||
{availableRanges[key].label}
|
||||
</option>
|
||||
) )}
|
||||
</select>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div className="burst-today">
|
||||
<div className="burst-today-select">
|
||||
<div className="burst-today-select-item">
|
||||
<Icon name={liveIcon} size="23"/>
|
||||
<h2>{live}</h2>
|
||||
<span><Icon name="live" size="12" color={'red'}/> {__( 'Live',
|
||||
'burst-statistics' )}</span>
|
||||
</div>
|
||||
<div className="burst-today-select-item">
|
||||
<Icon name={todayIcon} size="23"/>
|
||||
<h2>{data.today.value}</h2>
|
||||
<span><Icon name="total" size="13"
|
||||
color={'green'}/> {__( 'Total',
|
||||
'burst-statistics' )}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="burst-today-list">
|
||||
<div className="burst-today-list-item">
|
||||
<Icon name="winner"/>
|
||||
<p className="burst-today-list-item-text">{decodeURI(
|
||||
data.mostViewed.title )}</p>
|
||||
<p className="burst-today-list-item-number">{data.mostViewed.value}</p>
|
||||
</div>
|
||||
<div className="burst-today-list-item">
|
||||
<Icon name="referrer"/>
|
||||
<p className="burst-today-list-item-text">{decodeURI(
|
||||
data.referrer.title )}</p>
|
||||
<p className="burst-today-list-item-number">{data.referrer.value}</p>
|
||||
</div>
|
||||
<div className="burst-today-list-item">
|
||||
<Icon name="pageviews"/>
|
||||
<p className="burst-today-list-item-text">{data.pageviews.title}</p>
|
||||
<p className="burst-today-list-item-number">{data.pageviews.value}</p>
|
||||
</div>
|
||||
<div className="burst-today-list-item">
|
||||
<Icon name="time"/>
|
||||
<p className="burst-today-list-item-text">{data.timeOnPage.title}</p>
|
||||
<p className="burst-today-list-item-number">{data.timeOnPage.value}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</GridItem>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardWidget;
|
||||
@@ -0,0 +1,276 @@
|
||||
#dashboard_widget_burst {
|
||||
.inside{
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#dashboard-widgets .burst-today {
|
||||
.burst-grid-item-header {
|
||||
display:none;
|
||||
}
|
||||
|
||||
&-select {
|
||||
background: #ecf4ed;
|
||||
padding-inline: var(--rsp-spacing-l);
|
||||
padding-block: var(--rsp-spacing-m);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: var(--rsp-spacing-s);
|
||||
|
||||
&-item {
|
||||
border-radius: var(--rsp-border-radius-xs);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding-block: var(--rsp-spacing-m);
|
||||
justify-items: center;
|
||||
flex-wrap: wrap;
|
||||
background: var(--rsp-white);
|
||||
|
||||
|
||||
.burst-icon{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
&.active {
|
||||
box-shadow: inset 0 0 3px 2px var(--rsp-green-faded);
|
||||
border: 2px solid var(--rsp-green);
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: var(--rsp-spacing-xxs);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
gap: 3px;
|
||||
justify-content: center;
|
||||
font-size: var(--rsp-fs-100);
|
||||
|
||||
.burst-icon-live {
|
||||
animation-name: pulse;
|
||||
animation-duration: 1.5s;
|
||||
animation-timing-function: ease-in;
|
||||
animation-direction: alternate;
|
||||
animation-iteration-count: infinite;
|
||||
animation-play-state: running;
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(0.9);
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1.0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-list {
|
||||
width: 100%;
|
||||
|
||||
&-item {
|
||||
display: grid;
|
||||
justify-items: flex-start;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
gap: var(--rsp-spacing-s);
|
||||
padding-block: var(--rsp-spacing-xs);
|
||||
padding-inline: var(--rsp-spacing-l);
|
||||
|
||||
&:nth-of-type(even) {
|
||||
background: var(--rsp-grey-200);
|
||||
}
|
||||
|
||||
&-text {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
margin-right: auto;
|
||||
|
||||
}
|
||||
|
||||
&-number {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-controls-flex {
|
||||
display: flex;
|
||||
//justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--rsp-spacing-xs);
|
||||
|
||||
.burst-divider {
|
||||
width: 1px;
|
||||
height: 80%;
|
||||
background: var(--rsp-grey-500);
|
||||
}
|
||||
}
|
||||
.burst-grid-item-footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--rsp-grid-margin);
|
||||
width: 100%;
|
||||
min-height: calc(30px + var(--rsp-spacing-s) * 2);
|
||||
box-sizing: border-box;
|
||||
padding-inline: var(--rsp-spacing-l);
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
a.burst-button--secondary {
|
||||
background: var(--rsp-grey-100);
|
||||
color: var(--rsp-text-color-light);
|
||||
border: 1px solid var(--rsp-grey-400);
|
||||
}
|
||||
}
|
||||
|
||||
/* Global Variables */
|
||||
:root {
|
||||
--button-font-size: var(--rsp-fs-300);
|
||||
--button-font-weight: 400;
|
||||
--button-line-height: 2;
|
||||
--button-letter-spacing: 0.5px;
|
||||
--button-transition: all 0.3s ease;
|
||||
--button-min-height: 30px;
|
||||
--button-padding: 0 10px;
|
||||
--button-border-radius: 4px;
|
||||
--button-accent-color: #2271b1;
|
||||
--button-contrast-color: #000;
|
||||
--button-secondary-bg: #fff;
|
||||
}
|
||||
|
||||
/* Button Base Styles */
|
||||
a.burst-button, button.burst-button, input.burst-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--rsp-spacing-xxs);
|
||||
font-size: var(--button-font-size);
|
||||
font-weight: var(--button-font-weight);
|
||||
line-height: var(--button-line-height);
|
||||
letter-spacing: var(--button-letter-spacing);
|
||||
transition: var(--button-transition);
|
||||
min-height: var(--button-min-height);
|
||||
margin: 0;
|
||||
padding: var(--button-padding);
|
||||
border-radius: var(--button-border-radius);
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
|
||||
&--primary {
|
||||
background: var(--button-accent-color);
|
||||
color: var(--button-secondary-bg);
|
||||
border: 1px solid var(--button-accent-color);
|
||||
|
||||
&:hover {
|
||||
background: var(--button-accent-color);
|
||||
color: var(--button-secondary-bg);
|
||||
border-color: var(--button-accent-color);
|
||||
box-shadow: 0 0 0 3px rgba(34, 113, 177, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
&--secondary {
|
||||
background: var(--rsp-grey-100);
|
||||
color: var(--rsp-text-color-light);
|
||||
border: 1px solid var(--rsp-grey-400);
|
||||
|
||||
&:hover {
|
||||
background: var(--rsp-grey-200);
|
||||
color: var(--rsp-text-color);
|
||||
border-color: var(--rsp-grey-400);
|
||||
box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
&--tertiary {
|
||||
// red button
|
||||
background: var(--rsp-red);
|
||||
color: var(--rsp-text-color-white);
|
||||
border: 1px solid var(--rsp-red);
|
||||
|
||||
&:hover {
|
||||
background: var(--rsp-red-faded);
|
||||
color: var(--rsp-red);
|
||||
border-color: var(--rsp-red);
|
||||
box-shadow: 0 0 0 3px rgba(255, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
&--pro {
|
||||
background: var(--rsp-brand-primary);
|
||||
color: var(--rsp-text-color-white);
|
||||
border: 1px solid var(--rsp-brand-primary-dark);
|
||||
|
||||
&:hover {
|
||||
background: var(--rsp-brand-primary-dark);
|
||||
color: var(--button-secondary-bg);
|
||||
border-color: var(--rsp-brand-primary-darker);
|
||||
box-shadow: 0 0 0 3px var(--rsp-brand-primary-light);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.burst-button-icon {
|
||||
background: transparent;
|
||||
background: var(--rsp-grey-300);
|
||||
color: #2271b1;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 50%;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
padding: 10px;
|
||||
margin: 5px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
padding: 15px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&--delete {
|
||||
&:hover {
|
||||
background: var(--rsp-red-faded);
|
||||
|
||||
svg path {
|
||||
fill: var(--rsp-red);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.burst .burst-button.burst-button--date-range {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(---rsp-filter-padding);
|
||||
box-shadow: var(--rsp-box-shadow);
|
||||
cursor: pointer;
|
||||
gap: var(--rsp-spacing-xs);
|
||||
color: var(--rsp-text-color-light);
|
||||
background-color: var(--rsp-input-background-color);
|
||||
background: var(--rsp-grey-200);
|
||||
border: 1px solid var(--rsp-input-border-color);
|
||||
border-radius: var(--rsp-border-radius-xs);
|
||||
|
||||
.burst-icon {
|
||||
height: max-content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
render
|
||||
} from '@wordpress/element';
|
||||
import DashboardWidget from './components/DashboardWidget/DashboardWidget';
|
||||
import '../../assets/css/variables.scss';
|
||||
|
||||
import {
|
||||
QueryClient,
|
||||
QueryCache,
|
||||
QueryClientProvider
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
const HOUR_IN_SECONDS = 3600;
|
||||
const queryCache = new QueryCache({
|
||||
onError: ( error ) => {
|
||||
|
||||
// any error handling code...
|
||||
}
|
||||
});
|
||||
let config = {
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: HOUR_IN_SECONDS * 1000, // ms
|
||||
refetchOnWindowFocus: false,
|
||||
retry: false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// merge queryCache with config
|
||||
config = {...config, ...{queryCache}};
|
||||
|
||||
const queryClient = new QueryClient( config );
|
||||
document.addEventListener( 'DOMContentLoaded', () => {
|
||||
const container = document.getElementById( 'burst-widget-root' );
|
||||
if ( container ) {
|
||||
render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<DashboardWidget/>
|
||||
</QueryClientProvider>,
|
||||
container
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
const developmentConfig = require( './webpack.dev.js' );
|
||||
const productionConfig = require( './webpack.prod.js' );
|
||||
|
||||
const environment = process.env.BURST_ENV;
|
||||
|
||||
module.exports = 'production' === environment ? productionConfig : developmentConfig;
|
||||
@@ -0,0 +1,10 @@
|
||||
const defaultConfig = require( '@wordpress/scripts/config/webpack.config' );
|
||||
|
||||
module.exports = {
|
||||
...defaultConfig,
|
||||
output: {
|
||||
...defaultConfig.output,
|
||||
filename: '[name].js',
|
||||
chunkFilename: '[name].js'
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
const defaultConfig = require( '@wordpress/scripts/config/webpack.config' );
|
||||
|
||||
module.exports = {
|
||||
...defaultConfig,
|
||||
output: {
|
||||
...defaultConfig.output,
|
||||
filename: '[name].[contenthash].js',
|
||||
chunkFilename: '[name].[contenthash].js'
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user