first commit

This commit is contained in:
2026-04-28 15:13:50 +02:00
commit a95acc355b
63745 changed files with 9487948 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
import { __ } from '@wordpress/i18n';
import { useGetWidgetChart } from '../query/useGetWidgetChart';
import styles from './BarChartComponent.module.scss';
import { applyFilters } from '@wordpress/hooks';
import { Spinner } from '@wordpress/components';
import {
ResponsiveContainer,
BarChart,
Bar,
XAxis,
YAxis,
Tooltip,
CartesianGrid,
} from 'recharts';
import { useMemo } from 'react';
import dayjs from 'dayjs';
const start = dayjs().subtract( 30, 'day' ).format( 'YYYY-MM-DD' );
const end = dayjs().format( 'YYYY-MM-DD' );
function generateDateRange() {
const result = [];
let current = dayjs( start );
const last = dayjs( end );
while ( current.isBefore( last ) || current.isSame( last, 'day' ) ) {
const label = current.format( 'YYYY-MM-DD' );
current = current.add( 1, 'day' );
if ( ! result.includes( label ) ) {
result.push( label );
}
}
return result;
}
function buildChartData( currentData ) {
const currentRange = generateDateRange( start, end );
const currentMap = {};
currentData.forEach( ( item ) => {
const d = dayjs( item.date );
const key = d.format( 'YYYY-MM-DD' );
currentMap[ key ] = ( currentMap[ key ] || 0 ) + item.downloads;
} );
return currentRange.map( ( label, i ) => {
return {
date: label,
current: currentMap[ label ] || 0,
};
} );
}
function CustomTooltip( { active, payload, label } ) {
if ( ! active || ! payload?.length ) {
return null;
}
const current = payload.find( ( p ) => p.dataKey === 'current' )?.value ?? 0;
const formattedDate = dayjs( label ).format( 'D MMM' );
return (
<div className={ styles.tooltip }>
<div className={ styles.tooltipTitle }>{ __( 'Downloads', 'download-monitor' ) }</div>
<div className={ styles.tooltipWrap }>
<div className={ styles.tooltipDataWrap }>
<span className={ `${ styles.tip } ${ styles.tipCurrent }` } />
<div className={ styles.tooltipData }>
<span className={ styles.title }>{ formattedDate }</span>
<span className={ styles.value }>{ current.toLocaleString() }</span>
</div>
</div>
</div>
</div>
);
}
export default function BarChartComponent() {
const { data, isLoading, error } = useGetWidgetChart();
const chartData = useMemo( () => {
if ( ! data?.downloads_data ) {
return [];
}
return buildChartData( data.downloads_data );
}, [ data ] );
if ( isLoading ) {
return (
<>
{ applyFilters( 'dlm.widget.chart.before', '', { chartData } ) }
<div className={ styles.loading }>
<Spinner className={ styles.spinner } />
</div>
{ applyFilters( 'dlm.widget.chart.after', '', { chartData } ) }
</>
);
}
if ( error || ! chartData.length ) {
return <p>{ __( 'No chart data available.', 'download-monitor' ) }</p>;
}
return (
<div className={ styles.barChartWrapper }>
{ applyFilters( 'dlm.widget.chart.before', '', { chartData } ) }
<div className={ styles.barChart }>
<ResponsiveContainer height={ 200 }>
<BarChart
className={ styles.barChartTable }
onClick={ () => {
window.location.href = '/wp-admin/edit.php?post_type=dlm_download&page=download-monitor-reports&range=last30days';
}}
data={ chartData }>
<CartesianGrid horizontal={ true } vertical={ false } stroke="#f0f0f0" />
<XAxis
dataKey="date"
tick={ { fontSize: 12, fontWeight: 700 } }
tickFormatter={ ( value ) => dayjs( value ).format( 'D MMM' ) }
/>
<YAxis />
{ applyFilters( 'dlm.widget.chart.tooltip',
<Tooltip cursor={ { fill: 'rgba(0, 0, 0, 0.1)' } } content={ <CustomTooltip /> } />,
) }
<Bar dataKey="current" stackId="currentDownloads" fill="#31688e" name={ __( 'Current', 'download-monitor' ) } />
{ applyFilters( 'dlm.widget.chart', '' ) }
</BarChart>
</ResponsiveContainer>
</div>
{ applyFilters( 'dlm.widget.chart.after', '', { chartData } ) }
</div>
);
}

View File

@@ -0,0 +1,128 @@
.loading {
width: 100%;
height: 200px;
background-color: #fff;
padding: 24px 0;
overflow: hidden;
position: relative;
display: flex;
justify-content: center;
align-items: center;
.spinner {
width: 30px;
height: 30px;
}
}
.barChartWrapper {
background-color: #fff;
padding: 24px 0 0 0;
overflow: hidden;
position: relative;
padding: 12px;
background-color: #fff;
border: 1px solid #e0e0e0;
.barChart {
margin-left: -16px;
.barChartTable{
cursor: pointer;
}
}
}
.tooltip {
background: #FFF;
font-size: 14px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
padding: 17px 17px 17px;
display: grid;
gap: 10px;
min-width: 200px;
box-sizing: border-box;
line-height: 1;
.tooltipTitle {
text-align: left;
line-height: 18px;
width: 100%;
text-transform: uppercase;
font-size: 11px;
color: #757575;
margin-top: 0;
}
.tooltipWrap {
display: grid;
gap: 10px;
.tooltipDataWrap {
display: flex;
gap: 10px;
width: 100%;
align-items: center;
.tip {
display: block;
width: 16px;
height: 16px;
}
.tipCurrent {
background-color: #31688e;
}
.tipCompare {
background-color: #35b779;
}
.tooltipData {
display: flex;
justify-content: space-between;
flex: 1;
.title {
color: #3c434a;
font-size: 14px;
}
.value {
color: #3c434a;
font-size: 14px;
font-weight: 600;
}
}
}
}
.diff {
display: flex;
justify-content: center;
align-items: center;
font-weight: 600;
border-top: 1px solid #e0e0e0;
padding-top: 10px;
svg {
width: 16px;
height: 16px;
}
&.increase {
color: #12a26c;
svg {
fill: #12a26c;
}
}
&.decrease {
color: #7b0000;
svg {
fill: #7b0000;
}
}
}
}

View File

@@ -0,0 +1,20 @@
import styles from './Summary.module.scss';
import { applyFilters } from '@wordpress/hooks';
export default function Summary( { label = '', value = '', type = 'default', cards = {} } ) {
return (
<div className={ `${ styles.item } ${ styles[ type ] }` }>
<div className={ styles.content }>
<div className={ styles.label }>
{ label }
{ applyFilters( `dlm.widget.card.${ type }.label.after`, '', { type, cards } ) }
</div>
<div className={ styles.value }>
{ value }
{ applyFilters( `dlm.widget.card.${ type }.value.after`, '', { type, cards } ) }
</div>
{ applyFilters( `dlm.widget.card.${ type }.after`, '', { type, cards } ) }
</div>
</div>
);
}

View File

@@ -0,0 +1,28 @@
.item {
padding: 12px;
background-color: #fff;
border-bottom: 1px solid #e0e0e0;
border-right: 1px solid #e0e0e0;
.content {
.label {
display: flex;
margin-bottom: 14px;
font-size: 14px;
font-weight: normal;
line-height: 20px;
}
.value {
display: flex;
justify-content: space-between;
font-size: 18px;
font-weight: normal;
line-height: 24px;
}
}
&:not( :first-child) {
// border-left: unset;
}
}

View File

@@ -0,0 +1,28 @@
import { useGetWidgetCards } from '../query/useGetWidgetCards';
import styles from './WidgetCards.module.scss';
import { Spinner } from '@wordpress/components';
import { applyFilters } from '@wordpress/hooks';
import { __ } from '@wordpress/i18n';
import Summary from './Summary';
export default function WidgetCards() {
const {
data: cards,
isLoading,
} = useGetWidgetCards();
const total = isLoading ? <Spinner /> : ( cards?.total ?? 0 ).toLocaleString();
const today = isLoading ? <Spinner /> : ( cards?.today ?? 0 ).toLocaleString();
const popular = isLoading ? <Spinner /> : cards?.most_popular?.title || __( 'No Title', 'download-monitor' );
const average = isLoading ? <Spinner /> : ( cards?.average ?? 0 ).toLocaleString();
return (
<div className={ styles.dlmReportsCardsWrapper }>
<Summary label={ __( 'Today Downloads', 'download-monitor' ) } value={ today } type="today" cards={ cards } />
<Summary label={ __( 'Total Downloads', 'download-monitor' ) } value={ total } type="total" cards={ cards } />
<Summary label={ __( 'Daily Average', 'download-monitor' ) } value={ average } type="average" cards={ cards } />
<Summary label={ __( 'Most Popular', 'download-monitor' ) } value={ popular } type="popular" cards={ cards } />
{ applyFilters( 'dlm.widget.cards.after', '', { cards } ) }
</div>
);
}

View File

@@ -0,0 +1,8 @@
.dlmReportsCardsWrapper {
display: grid;
gap: 0;
margin-top: 12px;
grid-template-columns: 1fr 1fr;
border-top: 1px solid #e0e0e0;
border-left: 1px solid #e0e0e0;
}