This advanced widget creates a full monthly calendar view showing daily order counts and revenue with navigation arrows to browse different months. Perfect for getting a visual overview of your sales patterns throughout the month.
The snippet adds an interactive monthly calendar to your WordPress dashboard where each day shows the number of orders and total revenue. You can navigate between months using arrow buttons and see monthly summaries.
/**
* Monthly orders calendar widget for WordPress dashboard
* Shows daily orders and revenue in calendar format with navigation
* Prefix: tpsc_ (TP Snippet Collection)
*/
// ============================================
// ADD DASHBOARD WIDGET
// ============================================
// Add calendar widget to WordPress dashboard
add_action('wp_dashboard_setup', 'tpsc_add_calendar_orders_widget');
function tpsc_add_calendar_orders_widget() {
wp_add_dashboard_widget(
'tpsc_calendar_orders',
'📅 Monthly Orders Calendar',
'tpsc_calendar_orders_widget_content'
);
}
// ============================================
// WIDGET CONTENT
// ============================================
// Widget content function
function tpsc_calendar_orders_widget_content() {
// Check if WooCommerce is active
if (!class_exists('WooCommerce')) {
echo '<p>WooCommerce is not active.</p>';
return;
}
// Start with current month/year
$current_month = date('n');
$current_year = date('Y');
echo '<div class="tpsc-calendar-container">';
echo '<div id="tpsc-calendar-content">';
// Get and render initial calendar
$calendar_data = tpsc_get_calendar_data($current_month, $current_year);
echo tpsc_render_calendar_header($current_month, $current_year);
echo tpsc_render_calendar_grid($calendar_data, $current_month, $current_year);
echo tpsc_render_month_summary($calendar_data);
echo '</div>';
echo '<div id="tpsc-calendar-loading" style="display:none;">Loading...</div>';
echo '</div>';
}
// ============================================
// DATA COLLECTION
// ============================================
// Get calendar data for specific month
function tpsc_get_calendar_data($month, $year) {
$data = array();
// Get number of days in month
$days_in_month = date('t', mktime(0, 0, 0, $month, 1, $year));
// Loop through each day
for ($day = 1; $day <= $days_in_month; $day++) {
$date = sprintf('%04d-%02d-%02d', $year, $month, $day);
// Get orders for this date using WooCommerce functions (HPOS compatible)
$orders = wc_get_orders(array(
'status' => array('completed', 'processing', 'on-hold'),
'date_created' => $date,
'limit' => -1,
'return' => 'ids'
));
$orders_count = count($orders);
$revenue = 0;
// Calculate revenue
foreach ($orders as $order_id) {
$order = wc_get_order($order_id);
if ($order) {
$revenue += $order->get_total();
}
}
$data[$day] = array(
'date' => $date,
'orders' => $orders_count,
'revenue' => $revenue,
'formatted_revenue' => wc_price($revenue)
);
}
return $data;
}
// ============================================
// RENDER FUNCTIONS
// ============================================
// Render calendar header with navigation
function tpsc_render_calendar_header($month, $year) {
$month_name = date('F Y', mktime(0, 0, 0, $month, 1, $year));
// Check if we can navigate (don't go to future months)
$current_month = date('n');
$current_year = date('Y');
$can_go_next = false;
if ($year < $current_year || ($year == $current_year && $month < $current_month)) {
$can_go_next = true;
}
$prev_disabled = '';
$next_disabled = $can_go_next ? '' : 'disabled';
return '<div class="tpsc-calendar-header">
<button class="tpsc-nav-btn" id="tpsc-prev-btn" onclick="tpscNavigateMonth(-1)" ' . $prev_disabled . '>‹ Prev</button>
<h3 class="tpsc-month-title">' . $month_name . '</h3>
<button class="tpsc-nav-btn" id="tpsc-next-btn" onclick="tpscNavigateMonth(1)" ' . $next_disabled . '>Next ›</button>
<button class="tpsc-today-btn" onclick="tpscGoToToday()">Today</button>
</div>';
}
// Render calendar grid
function tpsc_render_calendar_grid($data, $month, $year) {
$output = '<div class="tpsc-calendar-grid">';
// Days of week header
$output .= '<div class="tpsc-calendar-header-row">';
$days = array('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
foreach ($days as $day) {
$output .= '<div class="tpsc-day-header">' . $day . '</div>';
}
$output .= '</div>';
// Get first day of month and what day of week it is
$first_day = mktime(0, 0, 0, $month, 1, $year);
$start_day_of_week = date('w', $first_day); // 0 = Sunday
$days_in_month = date('t', $first_day);
$output .= '<div class="tpsc-calendar-body">';
$day_count = 1;
// Calendar weeks (6 weeks max)
for ($week = 0; $week < 6; $week++) {
$output .= '<div class="tpsc-calendar-week">';
// Days in week
for ($day_of_week = 0; $day_of_week < 7; $day_of_week++) {
// Empty cells before first day
if ($week == 0 && $day_of_week < $start_day_of_week) {
$output .= '<div class="tpsc-calendar-day tpsc-empty-day"></div>';
}
// Days of the month
elseif ($day_count <= $days_in_month) {
$day_data = isset($data[$day_count]) ? $data[$day_count] : array('orders' => 0, 'revenue' => 0, 'formatted_revenue' => wc_price(0));
$today_class = '';
if ($day_count == date('j') && $month == date('n') && $year == date('Y')) {
$today_class = ' tpsc-today';
}
$has_orders_class = $day_data['orders'] > 0 ? ' tpsc-has-orders' : '';
$output .= '<div class="tpsc-calendar-day' . $today_class . $has_orders_class . '" data-date="' . $day_data['date'] . '">';
$output .= '<div class="tpsc-day-number">' . $day_count . '</div>';
if ($day_data['orders'] > 0) {
$output .= '<div class="tpsc-day-orders">' . $day_data['orders'] . ' orders</div>';
$output .= '<div class="tpsc-day-revenue">' . $day_data['formatted_revenue'] . '</div>';
}
$output .= '</div>';
$day_count++;
}
// Empty cells after last day
else {
$output .= '<div class="tpsc-calendar-day tpsc-empty-day"></div>';
}
}
$output .= '</div>';
// Stop if we've shown all days
if ($day_count > $days_in_month) {
break;
}
}
$output .= '</div></div>';
return $output;
}
// Render month summary
function tpsc_render_month_summary($data) {
$total_orders = 0;
$total_revenue = 0;
$days_with_orders = 0;
foreach ($data as $day_data) {
$total_orders += $day_data['orders'];
$total_revenue += $day_data['revenue'];
if ($day_data['orders'] > 0) {
$days_with_orders++;
}
}
$avg_daily_orders = $days_with_orders > 0 ? round($total_orders / $days_with_orders, 1) : 0;
return '<div class="tpsc-month-summary">
<div class="tpsc-summary-item">
<span class="tpsc-summary-label">Total Orders:</span>
<span class="tpsc-summary-value">' . number_format($total_orders) . '</span>
</div>
<div class="tpsc-summary-item">
<span class="tpsc-summary-label">Total Revenue:</span>
<span class="tpsc-summary-value">' . wc_price($total_revenue) . '</span>
</div>
<div class="tpsc-summary-item">
<span class="tpsc-summary-label">Active Days:</span>
<span class="tpsc-summary-value">' . $days_with_orders . '</span>
</div>
<div class="tpsc-summary-item">
<span class="tpsc-summary-label">Avg/Day:</span>
<span class="tpsc-summary-value">' . $avg_daily_orders . '</span>
</div>
</div>';
}
// ============================================
// AJAX HANDLERS
// ============================================
// AJAX handler for calendar navigation
add_action('wp_ajax_tpsc_load_calendar_month', 'tpsc_ajax_load_calendar_month');
function tpsc_ajax_load_calendar_month() {
// Check permissions
if (!current_user_can('read')) {
wp_die('Unauthorized');
}
// Check if WooCommerce is active
if (!class_exists('WooCommerce')) {
wp_send_json_error('WooCommerce is not active');
}
// Get and validate parameters
$month = isset($_POST['month']) ? intval($_POST['month']) : date('n');
$year = isset($_POST['year']) ? intval($_POST['year']) : date('Y');
// Validate month/year
if ($month < 1 || $month > 12) {
wp_send_json_error('Invalid month');
}
if ($year < 2020 || $year > date('Y')) {
wp_send_json_error('Invalid year');
}
// Don't allow future months
$current_month = date('n');
$current_year = date('Y');
if ($year > $current_year || ($year == $current_year && $month > $current_month)) {
wp_send_json_error('Cannot view future months');
}
// Get calendar data
$calendar_data = tpsc_get_calendar_data($month, $year);
// Generate HTML
$html = tpsc_render_calendar_header($month, $year);
$html .= tpsc_render_calendar_grid($calendar_data, $month, $year);
$html .= tpsc_render_month_summary($calendar_data);
wp_send_json_success(array(
'html' => $html,
'month' => $month,
'year' => $year,
'data' => $calendar_data
));
}
// Add widget styles and scripts
add_action('admin_enqueue_scripts', 'tpsc_calendar_widget_assets');
function tpsc_calendar_widget_assets($hook) {
// Only load on dashboard
if ($hook !== 'index.php') {
return;
}
// Calendar CSS
wp_add_inline_style('wp-admin', '
.tpsc-calendar-container {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.tpsc-calendar-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding: 10px 0;
border-bottom: 1px solid #ddd;
}
.tpsc-month-title {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #1d2327;
}
.tpsc-nav-btn, .tpsc-today-btn {
background: #2271b1;
color: white;
border: none;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
transition: background 0.2s;
}
.tpsc-nav-btn:hover, .tpsc-today-btn:hover {
background: #135e96;
}
.tpsc-today-btn {
background: #00a32a;
margin-left: 10px;
}
.tpsc-today-btn:hover {
background: #008a20;
}
.tpsc-nav-btn:disabled {
background: #ddd !important;
color: #999 !important;
cursor: not-allowed !important;
}
.tpsc-nav-btn:disabled:hover {
background: #ddd !important;
}
#tpsc-calendar-loading {
text-align: center;
padding: 40px;
color: #666;
font-style: italic;
}
.tpsc-calendar-fade {
opacity: 0.5;
pointer-events: none;
transition: opacity 0.3s ease;
}
.tpsc-calendar-grid {
border: 1px solid #ddd;
border-radius: 6px;
overflow: hidden;
}
.tpsc-calendar-header-row {
display: grid;
grid-template-columns: repeat(7, 1fr);
background: #f6f7f7;
}
.tpsc-day-header {
padding: 10px 5px;
text-align: center;
font-weight: 600;
font-size: 12px;
color: #50575e;
border-right: 1px solid #ddd;
}
.tpsc-day-header:last-child {
border-right: none;
}
.tpsc-calendar-body {
background: white;
}
.tpsc-calendar-week {
display: grid;
grid-template-columns: repeat(7, 1fr);
border-bottom: 1px solid #ddd;
}
.tpsc-calendar-week:last-child {
border-bottom: none;
}
.tpsc-calendar-day {
min-height: 80px;
padding: 8px;
border-right: 1px solid #ddd;
position: relative;
background: white;
transition: background 0.2s;
}
.tpsc-calendar-day:last-child {
border-right: none;
}
.tpsc-calendar-day:hover {
background: #f8f9fa;
}
.tpsc-empty-day {
background: #f9f9f9;
}
.tpsc-today {
background: #e7f3ff !important;
border: 2px solid #2271b1 !important;
}
.tpsc-has-orders {
background: #f0f9ff;
}
.tpsc-day-number {
font-weight: 600;
font-size: 14px;
color: #1d2327;
margin-bottom: 4px;
}
.tpsc-today .tpsc-day-number {
color: #2271b1;
}
.tpsc-day-orders {
font-size: 11px;
color: #2271b1;
font-weight: 600;
margin-bottom: 2px;
}
.tpsc-day-revenue {
font-size: 10px;
color: #50575e;
font-weight: 500;
}
.tpsc-month-summary {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
margin-top: 15px;
padding: 15px;
background: #f8f9fa;
border-radius: 6px;
}
.tpsc-summary-item {
text-align: center;
}
.tpsc-summary-label {
display: block;
font-size: 11px;
color: #50575e;
margin-bottom: 4px;
font-weight: 500;
}
.tpsc-summary-value {
display: block;
font-size: 16px;
font-weight: 600;
color: #1d2327;
}
/* Mobile responsive */
@media (max-width: 768px) {
.tpsc-calendar-day {
min-height: 60px;
padding: 4px;
}
.tpsc-day-number {
font-size: 12px;
}
.tpsc-day-orders {
font-size: 9px;
}
.tpsc-day-revenue {
font-size: 8px;
}
.tpsc-month-summary {
grid-template-columns: repeat(2, 1fr);
}
.tpsc-calendar-header {
flex-wrap: wrap;
gap: 10px;
}
.tpsc-month-title {
order: -1;
width: 100%;
text-align: center;
}
}
');
// Calendar JavaScript
wp_add_inline_script('jquery', '
// Global variables
window.tpscCurrentMonth = ' . date('n') . ';
window.tpscCurrentYear = ' . date('Y') . ';
window.tpscCalendarData = {};
window.tpscIsLoading = false;
// Make functions global
window.tpscNavigateMonth = function(direction) {
if (window.tpscIsLoading) return;
var newMonth = window.tpscCurrentMonth + direction;
var newYear = window.tpscCurrentYear;
if (newMonth < 1) {
newMonth = 12;
newYear--;
} else if (newMonth > 12) {
newMonth = 1;
newYear++;
}
// Don\'t allow future months
var currentRealMonth = new Date().getMonth() + 1;
var currentRealYear = new Date().getFullYear();
if (newYear > currentRealYear || (newYear == currentRealYear && newMonth > currentRealMonth)) {
return; // Block navigation to future
}
tpscLoadCalendarMonth(newMonth, newYear);
};
window.tpscGoToToday = function() {
if (window.tpscIsLoading) return;
var today = new Date();
var currentMonth = today.getMonth() + 1;
var currentYear = today.getFullYear();
tpscLoadCalendarMonth(currentMonth, currentYear);
};
function tpscLoadCalendarMonth(month, year) {
if (window.tpscIsLoading) return;
window.tpscIsLoading = true;
// Show loading state
var content = document.getElementById("tpsc-calendar-content");
var loading = document.getElementById("tpsc-calendar-loading");
if (content) content.classList.add("tpsc-calendar-fade");
if (loading) loading.style.display = "block";
// AJAX request using jQuery
jQuery.ajax({
url: ajaxurl,
type: "POST",
data: {
action: "tpsc_load_calendar_month",
month: month,
year: year
},
success: function(response) {
window.tpscIsLoading = false;
if (loading) loading.style.display = "none";
if (response.success) {
// Update calendar
if (content) {
content.innerHTML = response.data.html;
content.classList.remove("tpsc-calendar-fade");
}
// Update global variables
window.tpscCurrentMonth = response.data.month;
window.tpscCurrentYear = response.data.year;
window.tpscCalendarData = response.data.data;
// Re-add event handlers
tpscAddDayClickHandlers();
} else {
alert("Error: " + (response.data || "Unknown error"));
if (content) content.classList.remove("tpsc-calendar-fade");
}
},
error: function() {
window.tpscIsLoading = false;
if (loading) loading.style.display = "none";
if (content) content.classList.remove("tpsc-calendar-fade");
alert("Network error");
}
});
}
function tpscAddDayClickHandlers() {
const calendarDays = document.querySelectorAll(".tpsc-calendar-day[data-date]");
calendarDays.forEach(function(day) {
day.addEventListener("click", function() {
const date = this.getAttribute("data-date");
tpscShowDayDetails(date, this);
});
});
}
function tpscShowDayDetails(date, element) {
// Remove existing tooltips
const existingTooltips = document.querySelectorAll(".tpsc-day-tooltip");
existingTooltips.forEach(tooltip => tooltip.remove());
// Get day data
if (!window.tpscCalendarData) return;
const day = parseInt(date.split("-")[2]);
const dayData = window.tpscCalendarData[day];
if (!dayData || dayData.orders === 0) return;
// Create tooltip
const tooltip = document.createElement("div");
tooltip.className = "tpsc-day-tooltip";
tooltip.innerHTML = `
<strong>${date}</strong><br>
Orders: ${dayData.orders}<br>
Revenue: ${dayData.formatted_revenue}
`;
// Style tooltip
tooltip.style.cssText = `
position: absolute;
background: #1d2327;
color: white;
padding: 10px;
border-radius: 4px;
font-size: 12px;
z-index: 1000;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
pointer-events: none;
white-space: nowrap;
`;
// Position tooltip
document.body.appendChild(tooltip);
const rect = element.getBoundingClientRect();
tooltip.style.left = (rect.left + window.scrollX + rect.width / 2 - tooltip.offsetWidth / 2) + "px";
tooltip.style.top = (rect.top + window.scrollY - tooltip.offsetHeight - 10) + "px";
// Remove tooltip after 3 seconds
setTimeout(() => {
tooltip.remove();
}, 3000);
}
// Initialize on page load
jQuery(document).ready(function() {
tpscAddDayClickHandlers();
});
');
}
Something not working as expected? Need help customizing it for your specific needs?
Have an idea for functionality you're missing on your site? Tell us what you're looking for!
Share it with us! Our community loves learning and growing together.
No comments yet.