feat(panel-admin): Marketing cluster with Discord Dashboard#260
Conversation
…ta lake Adds a discord_event_logs table and a raw gateway listener that persists every Discord event (MESSAGE_CREATE, VOICE_STATE_UPDATE, GUILD_MEMBER_ADD, etc.) with full payload for future analytics. Registered via Laracord AFTER_BOOT hook on the Discord client.
Introduce a Marketing navigation cluster in the admin panel with two pages: Discord Dashboard (community analytics) and Meeting Showcase (moved from top-level pages). The dashboard surfaces Discord activity metrics with configurable time ranges, rolling period comparison, heatmap, and channel rankings using hardcoded data as a prototype for the query layer. - Add MarketingCluster with dedicated sidebar navigation - Add DiscordDashboard page with stats widget, timeline chart, heatmap, activity-by-DOW with All/Msgs/Voice toggle, top channels, and period breakdown with 5 switchable views (summary, table, cards, bars, donut) - Add DiscordStatsWidget using Filament StatsOverviewWidget with sparklines - Extend bar-chart Blade component with stacked mode (segments + legend) - Move MeetingShowcasePage into Marketing cluster - Add CONTEXT.md and ADR-0001 for panel-admin module
…Stan Replace hardcoded dashboard data with query classes that hit the database. Each query uses AT TIME ZONE 'UTC' AT TIME ZONE 'America/Sao_Paulo' for correct timezone conversion on timestamp-without-timezone columns. - Add 6 query classes: ActivityPerDay, VoicePerDay, MessageHeatmap, VoiceHeatmap, TopChannels, PeriodStats - PeriodStats uses conditional aggregates (CASE WHEN) to fetch all sub-periods in 2 queries instead of N×3, with once() memoization - Fix division-by-zero in DiscordStatsWidget when previous period is zero - Fix MeetingShowcasePage PHPStan error (groupBy returns stdClass, not Model) - Fix RawGatewayEvent unnecessary nullsafe operator before null coalescing - Remove prototype HTML file - Add ADR reference to panel-admin CONTEXT.md
- Remove meeting-showcase.html and voice-chat-2026-05-11.json from tracked files - Add PHPDoc object shape annotation to fix undefined property access on groupBy result
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Repository YAML (base), Central YAML (inherited) Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR introduces a complete marketing admin panel alongside the existing moderation interface. The implementation adds a Discord activity dashboard featuring timeline and heatmap visualizations, rolling-period comparisons with stat cards, per-day-of-week activity breakdowns, and top-channel rankings. A meeting showcase feature enables participants to be loaded from message data by date and channel, rendered in a customizable grid, and exported as PNG. The bar chart component gains stacked segment support with optional legend rendering. Event persistence is enhanced with an occurred_at timestamp column on discord_event_logs. Documentation includes a module context file, an architecture decision record defining the dashboard design, and translations for the marketing section. Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app-modules/bot-discord/tests/Feature/Events/RawGatewayEventTest.php (1)
36-55:⚠️ Potential issue | 🟠 Major | ⚡ Quick winExtend user_id extraction to support
d.user.idfield path and add regression test.The current implementation only extracts from
d.user_idandd.author.id, but Discord Gateway events with a nesteduserobject used.user.idfor the user identity. Update line 20 of RawGatewayEvent.php to include this fallback:$payload->d->user_id ?? $payload->d->author->id ?? $payload->d->user->id ?? null, then add a test case covering thed.user.idextraction path to prevent regression.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app-modules/bot-discord/tests/Feature/Events/RawGatewayEventTest.php` around lines 36 - 55, RawGatewayEvent currently only checks d->user_id and d->author->id when extracting the user id; update the extraction in RawGatewayEvent (where it builds $userId from the payload) to also fallback to $payload->d->user->id (i.e. $payload->d->user_id ?? $payload->d->author->id ?? $payload->d->user->id ?? null) and add a PHPUnit test in the RawGatewayEventTest that constructs a payload with d.user.id set and asserts the created DiscordEventLog->user_id matches that value to prevent regression.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app-modules/bot-discord/src/Events/RawGatewayEvent.php`:
- Line 20: Restore the missing fallback for extracting user_id in
RawGatewayEvent.php: when building the 'user_id' field (currently using
$payload->d->user_id ?? $payload->d->author->id ?? null), add the
$payload->d->user->id fallback so member events (GUILD_MEMBER_ADD /
GUILD_MEMBER_UPDATE) are handled; update the expression that constructs
'user_id' to include $payload->d->user->id in the fallback chain (reference: the
'user_id' assignment in RawGatewayEvent.php).
In
`@app-modules/integration-discord/database/migrations/2026_05_19_155732_create_discord_event_logs_table.php`:
- Line 23: The migration defines the occurred_at column using timestamp(), which
creates a timezone-naive TIMESTAMP; change the column definition to use
timestampTz() (i.e., replace the call to timestamp('occurred_at') with
timestampTz('occurred_at')->index()) so PostgreSQL uses TIMESTAMP WITH TIME ZONE
and avoids UTC/BRT conversion errors when reading/writing the occurred_at field
in the Discord event logs migration.
In `@app-modules/panel-admin/CONTEXT.md`:
- Around line 28-54: The fenced code block in CONTEXT.md (the directory tree
example) is missing a language tag which triggers MD040; update the opening
fence from ``` to ```text so the block is explicitly marked as plain text (leave
the closing fence as ```), ensuring the lint rule passes and the docs render
consistently.
In `@app-modules/panel-admin/docs/adr/0001-discord-dashboard-architecture.md`:
- Line 48: Update the ADR text that currently references the old query location
so it matches the implemented classes: change the documented path
"Marketing/Discord/Queries/" to the actual location
"Marketing/Pages/Discord/Dashboard/Queries/"; keep the rest of the sentence
about each widget having a final readonly query class with a builder() method
and the Page instantiating queries and combining heatmap data from separate
message/voice query classes so the documentation correctly points to the
implemented classes used by the Page.
In
`@app-modules/panel-admin/resources/views/marketing/discord-dashboard.blade.php`:
- Around line 148-154: The chart data is truncating fractional voice-hours by
casting $b['voice'] to (int); update every occurrence (e.g., the combined
'value' => $b['msgs'] + (int) $b['voice'] and the segments entry ['label' =>
'Voice', 'value' => (int) $b['voice']]) to preserve fractions — remove the (int)
cast and instead use the original numeric value or a controlled float like
round($b['voice'], 2) so the combined value and the 'Voice' segment reflect
fractional hours accurately.
- Around line 220-233: The chart currently hardcodes w1 = this.tl.slice(0,7) and
w2 = this.tl.slice(7,14) which breaks for ranges other than 14 days; change the
splitting logic to compute a dynamic half based on this.tl.length (e.g. const
half = Math.ceil(this.tl.length / 2)), set const w1 = this.tl.slice(0, half) and
const w2 = this.tl.slice(half, half + half) (or to end) and derive labels from
w1 (or from dates in this.tl) instead of the fixed days array, then map datasets
(Msgs and Voice) from w1.map(...) and w2.map(...) and, if needed, pad w2 with
nulls/zeros so both datasets align; update the Chart creation (sChart = new
Chart(...)) to use these computed labels and arrays so Sem 1 / Sem 2 reflect the
selected range correctly.
In `@app-modules/panel-admin/resources/views/pages/meeting-showcase.blade.php`:
- Around line 104-113: In the html2canvas call that creates the canvas (the
block using html2canvas with options scale, useCORS, allowTaint), remove or set
allowTaint to false so the canvas is not tainted before calling
canvas.toDataURL('image/png'); keep useCORS: true and ensure remote images have
proper CORS headers (or route them through a proxy) so images load without
tainting; update the options passed to html2canvas and verify canvas.toDataURL
and link.click still work after this change.
In
`@app-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/DiscordDashboard.php`:
- Line 23: The public property rangeDays is client-hydrated and must be clamped
to an allowlist before queries to prevent tampered large windows; in mount() and
in updatedRangeDays() normalize/validate rangeDays against a predefined
allowlist (e.g., an array of allowed integers) and set it to a safe default if
invalid, then call loadData() only after the normalized value is assigned; also
apply the same normalization wherever rangeDays is used (references: property
rangeDays, methods mount(), updatedRangeDays(), and loadData()).
- Around line 144-154: Replace the hard-coded Portuguese weekday abbreviations
in the $dayLabels array with localized strings by loading translation keys and
using the app's translation helper (e.g. __() or trans()) inside the
DiscordDashboard code where $dayLabels is defined; update the $dayLabels
assignment to pull keys like 'weekdays.mon'...'weekdays.sun' (or your chosen
keys) so the existing foreach that builds $this->activityByDow (using $msgsByDow
and $voiceByDow) continues to use the same indexes and structure but outputs
localized labels.
In `@app-modules/panel-admin/src/Marketing/Pages/MeetingShowcasePage.php`:
- Around line 61-68: The result of Message::query()->...->get() is inferred as
Collection<Message>, so the aggregate column total_messages isn't a declared
model property; update the code that builds $messageStats to fetch base-query
rows instead (e.g., replace the terminal ->get() with ->getQuery()->get() so
$messageStats becomes a collection of stdClass rows exposing total_messages) or,
if you must keep Eloquent models, access the aggregate via
$row->getAttribute('total_messages') when reading the value; adjust the
Message::query() block (and the code that reads $messageStats) accordingly.
- Around line 54-56: The early-return branch in MeetingShowcasePage that checks
if $this->channelId, $this->startDate, or $this->endDate are empty should clear
any previously loaded state before returning; update the block surrounding the
if ($this->channelId === '' || $this->startDate === '' || $this->endDate === '')
check to reset the page's loaded data (for example, set $this->participants to
an empty array and clear any result/loaded flags such as $this->loaded or
$this->hasResults) so stale participants or flags are not left visible when
filters are incomplete.
In `@app-modules/panel-admin/src/PanelAdminServiceProvider.php`:
- Line 83: The route check currently uses $requestPath->contains('marketing/')
so requests to '.../marketing' won't match; replace that condition to check the
marketing prefix (e.g. use $requestPath->startsWith('marketing') or a regex that
matches '^marketing(/|$)') so both the marketing root and nested marketing/*
routes invoke marketingNavigation($builder); update the condition where
$requestPath->contains('marketing/') is used to this new startsWith/regex-based
check.
In `@storage/private/dumps/meeting-showcase.html`:
- Around line 598-609: The current grid.innerHTML = users.map(...) inserts
untrusted fields (name, username, avatar_url) directly into HTML causing XSS;
replace this with DOM construction inside the users.map loop:
createElement('div') for the "user-card", create an img element and set img.src
= avatar || placeholder via setAttribute or assignment, attach an
img.addEventListener('error', ...) to set a safe fallback, and set
name/username/msgs via textContent (not innerHTML) on span elements; then append
each card to grid (or use a DocumentFragment) instead of assigning innerHTML.
Ensure you remove any inline event handlers/markup injection and only use safe
attribute setters and textContent for the variables name, uname and msgs.
In `@storage/private/dumps/voice-chat-2026-05-11.json`:
- Around line 1-408: The committed JSON "voice-chat-2026-05-11.json" contains
raw PII and must be removed: delete the file from the repo history and working
tree (use git rm --cached then commit), add the filename to your repo .gitignore
to prevent re-adding, replace the tracked file with a sanitized schema-only
example if an example is needed, and move the real export to secure
non-versioned storage (and if the data was shared, follow your org's
data-exposure remediation such as rotating any exposed credentials). If the file
must be purged from history, run a history-rewrite tool (git filter-repo or BFG)
to remove all historical commits containing voice-chat-2026-05-11.json.
---
Outside diff comments:
In `@app-modules/bot-discord/tests/Feature/Events/RawGatewayEventTest.php`:
- Around line 36-55: RawGatewayEvent currently only checks d->user_id and
d->author->id when extracting the user id; update the extraction in
RawGatewayEvent (where it builds $userId from the payload) to also fallback to
$payload->d->user->id (i.e. $payload->d->user_id ?? $payload->d->author->id ??
$payload->d->user->id ?? null) and add a PHPUnit test in the RawGatewayEventTest
that constructs a payload with d.user.id set and asserts the created
DiscordEventLog->user_id matches that value to prevent regression.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Central YAML (inherited)
Review profile: CHILL
Plan: Pro
Run ID: a891ea0c-5f08-4155-8b06-b2bc01ac45da
📒 Files selected for processing (27)
.gitignoreCONTEXT-MAP.mdapp-modules/bot-discord/src/Events/RawGatewayEvent.phpapp-modules/bot-discord/tests/Feature/Events/RawGatewayEventTest.phpapp-modules/he4rt/resources/views/components/dashboard/bar-chart.blade.phpapp-modules/integration-discord/database/migrations/2026_05_19_155732_create_discord_event_logs_table.phpapp-modules/integration-discord/src/Models/DiscordEventLog.phpapp-modules/integration-discord/tests/Feature/Models/DiscordEventLogTest.phpapp-modules/panel-admin/CONTEXT.mdapp-modules/panel-admin/docs/adr/0001-discord-dashboard-architecture.mdapp-modules/panel-admin/lang/en/marketing.phpapp-modules/panel-admin/lang/pt_BR/marketing.phpapp-modules/panel-admin/resources/views/marketing/discord-dashboard.blade.phpapp-modules/panel-admin/resources/views/pages/meeting-showcase.blade.phpapp-modules/panel-admin/src/Marketing/MarketingCluster.phpapp-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/DiscordDashboard.phpapp-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/ActivityPerDay.phpapp-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/MessageHeatmap.phpapp-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/PeriodStats.phpapp-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/TopChannels.phpapp-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/VoiceHeatmap.phpapp-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/VoicePerDay.phpapp-modules/panel-admin/src/Marketing/Pages/MeetingShowcasePage.phpapp-modules/panel-admin/src/Marketing/Widgets/DiscordStatsWidget.phpapp-modules/panel-admin/src/PanelAdminServiceProvider.phpstorage/private/dumps/meeting-showcase.htmlstorage/private/dumps/voice-chat-2026-05-11.json
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app-modules/panel-admin/src/Marketing/Pages/MeetingShowcasePage.php`:
- Around line 58-59: The start/end date parsing currently converts local
midnights to UTC, excluding most of the selected end day; update the Date::parse
calls in MeetingShowcasePage.php so you normalize to the local day's boundaries
(call startOfDay() on the start Date and endOfDay() on the end Date) before
calling utc(), i.e. modify the logic that sets $start and $end (the variables
produced from Date::parse($this->startDate, 'America/Sao_Paulo') and
Date::parse($this->endDate, 'America/Sao_Paulo')) to use startOfDay() and
endOfDay() respectively and then convert to UTC so whereBetween covers the full
selected end day.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Central YAML (inherited)
Review profile: CHILL
Plan: Pro
Run ID: 895597eb-9ab4-48b3-809f-c228619e40f9
📒 Files selected for processing (1)
app-modules/panel-admin/src/Marketing/Pages/MeetingShowcasePage.php
…s PHPStan on groupBy result
…d_event_logs The column was never migrated to the database. Remove from model fillable, casts, migration definition, and test fixtures. The occurred_at references in DTOs and Actions are for other tables (voice_messages, moderation_events) and remain unchanged.
|
10/10 |
|
LGTM! |
## Summary
- Adds `app.display_timezone` config key (`DISPLAY_TIMEZONE` env,
default `America/Sao_Paulo`) to centralize the user-facing timezone
- Changes `app.timezone` default from `America/Sao_Paulo` to `UTC`
- Replaces all 15 hardcoded `'America/Sao_Paulo'` occurrences across 15
files with `config('app.display_timezone')`
- Uses parameterized SQL bindings (`?`) for `AT TIME ZONE` clauses
instead of string interpolation
- Adds `AT TIME ZONE` conversion to moderation heatmap SQL that was
missing it entirely
- Converts all user-facing timestamp displays (SLA deadlines, connection
dates, schedule cards) to use the display timezone
## Modules affected
- `config/app.php` — new config key
- `bot-discord` — GreetingsEvent
- `he4rt` — heatmap highlight + schedule-card
- `panel-admin` — marketing dashboard queries (6 files) + moderation
heatmap + appeal views
- `integration-discord` — BackfillVoiceLogsCommand CLI output
- `routes/console.php` — backup:monitor schedule
- `resources/views` — connection-hub
## Test plan
- [x] Rector dry-run — no suggestions
- [x] Pint — passed
- [x] PHPStan — 0 errors
- [x] Tests — 497 passed, 3 skipped
- [x] Verified `AT TIME ZONE ?` binding works via tinker
- [ ] Verify heatmaps render correctly in the admin panel
- [ ] Verify SLA deadline times display in Brazil timezone
- [ ] Verify schedule-card shows correct local times
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Description
Centralizes the user-facing timezone by adding app.display_timezone (env
DISPLAY_TIMEZONE, default America/Sao_Paulo) and switching the
application default timezone to UTC. Replaces hard-coded
America/Sao_Paulo occurrences with config('app.display_timezone'),
parameterizes SQL AT TIME ZONE bindings, fixes a missing AT TIME ZONE in
the moderation heatmap, and converts user-facing timestamp displays to
the new display timezone.
## References
- PR: #262
- PR: #260
- PostgreSQL AT TIME ZONE docs:
https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-TIMEZONES
## Dependencies & Requirements
- New config key: app.display_timezone (env: DISPLAY_TIMEZONE, default:
America/Sao_Paulo)
- app.timezone default changed to UTC (env: APP_TIMEZONE)
- .env.example updated with APP_TIMEZONE=UTC and
DISPLAY_TIMEZONE=America/Sao_Paulo
- No new composer/npm packages added
- Database must accept parameterized AT TIME ZONE bindings (validated
via tinker)
- Manual verifications pending: admin heatmaps rendering, SLA deadline
display, schedule-card local times
## Contributor Summary
| Contributor | Lines Added | Lines Removed | Files Changed |
|---|---:|---:|---:|
| gvieira18 | 63 | 45 | 15 |
## Changes Summary
| File Path | Change Description |
|---|---|
| config/app.php | Added display_timezone key; changed default
app.timezone to UTC |
| .env.example | Added APP_TIMEZONE and DISPLAY_TIMEZONE entries |
| app-modules/bot-discord/src/Events/GreetingsEvent.php | Use
display_timezone for greeting-hour calculation |
|
app-modules/he4rt/resources/views/components/dashboard/heatmap.blade.php
| Heatmap current-time highlight uses display_timezone |
| app-modules/he4rt/resources/views/components/schedule-card.blade.php |
Normalize schedule timestamps to display_timezone |
|
app-modules/integration-discord/src/ETL/Console/BackfillVoiceLogsCommand.php
| Format CLI log timestamps with display_timezone |
|
app-modules/panel-admin/resources/views/moderation/appeal-queue/appeal-detail.blade.php
| SLA deadline displays converted to display_timezone with null fallback
|
|
app-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/ActivityPerDay.php
| Use display_timezone and bind into SQL AT TIME ZONE |
|
app-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/MessageHeatmap.php
| Parameterized timezone bindings for day/hour extraction |
|
app-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/VoiceHeatmap.php
| Parameterized timezone bindings in voice heatmap query |
|
app-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/VoicePerDay.php
| Day-extraction query updated to use display_timezone |
|
app-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/PeriodStats.php
| Compute subdivisions using display_timezone |
|
app-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/TopChannels.php
| Date range calculations use display_timezone |
| app-modules/panel-admin/src/Marketing/Pages/MeetingShowcasePage.php |
Parse meeting bounds with display_timezone and convert to UTC |
|
app-modules/panel-admin/src/Moderation/Livewire/ModerationDashboardLivewire.php
| Added AT TIME ZONE conversion and parameterized bindings for
moderation heatmap |
| resources/views/livewire/connection-hub.blade.php | "Connected at"
timestamp uses display_timezone; props formatting |
| routes/console.php | Schedule backup:monitor to run at 09:00 in
display_timezone |
|
database/migrations/2026_05_20_160249_alter_messages_sent_at_to_timestamptz.php
| Migration: convert messages.sent_at to timestamptz interpreting
existing values as UTC |
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/he4rt/heartdevs.com/pull/264?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: danielhe4rt <danielhe4rt@gmail.com>
Summary
ActivityPerDay,VoicePerDay,MessageHeatmap,VoiceHeatmap,TopChannels,PeriodStats) with proper UTC→BRT timezone handlingx-he4rt::dashboard.bar-chartcomponent with backward-compatible stacked modeCONTEXT.mdandADR-0001for panel-admin moduleDashboard widgets
Performance
CASE WHENconditional aggregates to fetch all sub-periods in 2 queries instead of N×3once()memoization prevents duplicate query execution within a requestTest plan
/admin/marketing/discordand verify all widgets render with real data/admin/marketing/meeting-showcaseDescription
This PR adds a Marketing cluster to the admin panel with a comprehensive Discord Dashboard providing real-time community analytics across messages, voice activity, and user engagement. Six query classes handle activity metrics with UTC↔BRT timezone conversion, the bar-chart component is extended with backward-compatible stacked mode support, and two new pages deliver Discord Dashboard analytics and Meeting Showcase participant visualization. Includes documentation, English/Portuguese-Brazil translations, and performance optimizations using conditional aggregates and memoization.
References
#259)Dependencies & Requirements
occurred_attimestamp (indexed) to discord_event_logs tableContributor Summary
Changes Summary
.gitignore/storage/private/dumpsdirectory to ignore patternsCONTEXT-MAP.mdapp-modules/bot-discord/src/Events/RawGatewayEvent.phpapp-modules/bot-discord/tests/Feature/Events/RawGatewayEventTest.phpapp-modules/he4rt/resources/views/components/dashboard/bar-chart.blade.phpstacked,legend,segmentsprops with conditional renderingapp-modules/integration-discord/database/migrations/2026_05_19_155732_create_discord_event_logs_table.phpoccurred_attimestamp columnapp-modules/integration-discord/src/Models/DiscordEventLog.phpoccurred_atto fillable and datetime castingapp-modules/integration-discord/tests/Feature/Models/DiscordEventLogTest.phpoccurred_atduring record creationapp-modules/panel-admin/CONTEXT.mdapp-modules/panel-admin/docs/adr/0001-discord-dashboard-architecture.mdapp-modules/panel-admin/lang/en/marketing.phpapp-modules/panel-admin/lang/pt_BR/marketing.phpapp-modules/panel-admin/resources/views/marketing/discord-dashboard.blade.phpapp-modules/panel-admin/resources/views/pages/meeting-showcase.blade.phpapp-modules/panel-admin/src/Marketing/MarketingCluster.phpapp-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/DiscordDashboard.phpapp-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/ActivityPerDay.phpapp-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/MessageHeatmap.phpapp-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/PeriodStats.phpapp-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/TopChannels.phpapp-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/VoiceHeatmap.phpapp-modules/panel-admin/src/Marketing/Pages/Discord/Dashboard/Queries/VoicePerDay.phpapp-modules/panel-admin/src/Marketing/Pages/MeetingShowcasePage.phpapp-modules/panel-admin/src/Marketing/Widgets/DiscordStatsWidget.phpapp-modules/panel-admin/src/PanelAdminServiceProvider.php