feat: Texty 2.0 revamp with new features#57
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughRegisters a Metrics REST controller and dashboard; changes gateway send() success returns to structured arrays; logs SMS send results into a new Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client App
participant Tools as Tools.php
participant Gateway as Gateway
participant Dispatcher as Dispatcher
participant DB as Database
Client->>Tools: request send SMS (to, message)
Tools->>Gateway: send(to, message)
Gateway-->>Tools: return WP_Error OR { success, reference_id }
Tools->>Dispatcher: do_action('texty_after_send_sms', result, to, message, gateway)
Dispatcher->>DB: INSERT INTO texty_sms_stat(receiver,gateway,status,reference_id,created_at,updated_at)
DB-->>Dispatcher: insert result
Tools-->>Client: return send result
sequenceDiagram
participant Browser as Browser
participant Dashboard as Dashboard.js
participant WPAPI as WordPress REST API
participant Metrics as Metrics.php
participant DB as Database
Browser->>Dashboard: mount
Dashboard->>WPAPI: GET /texty/v1/metrics
WPAPI->>Metrics: handle request -> get_metrics
Metrics->>DB: queries for monthly, last_month, 30-day delivery, 12-month aggregates
DB-->>Metrics: query results
Metrics-->>WPAPI: metrics payload
WPAPI-->>Dashboard: metrics JSON
Dashboard->>Browser: render StatCards + VolumeChart + QuickSend
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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: 10
♻️ Duplicate comments (2)
includes/Api/Tools.php (1)
64-68: Duplicate logging logic — same asSend.php.This block is identical to the one in
includes/Api/Send.php(lines 69–73). See the refactoring suggestion in theSend.phpreview to centralize this intoDispatcher::maybe_log_sms().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Api/Tools.php` around lines 64 - 68, The SMS-success logging block in Tools.php duplicates the logic in Send.php; replace the inline check and call to Dispatcher::log_sms(...) with a single call to the centralized helper Dispatcher::maybe_log_sms($to, $status) (created per the Send.php review), and ensure maybe_log_sms implements the status validation (is_wp_error, is_array, isset success) and calls Dispatcher::log_sms($to, $reference_id) internally so this file no longer repeats the conditional logic.includes/Dispatcher.php (1)
87-104:⚠️ Potential issue | 🔴 CriticalColumn name
timestampdoes not exist in the table schema — see related comment onInstall.php.The insert on line 97 references a
timestampcolumn, butInstall.php::create_tables()definescreated_atandupdated_atinstead. Additionally,updated_atisNOT NULLwith no default and is never provided. This will cause every$wpdb->insert()call to fail. See the detailed fix options in theInstall.phpreview comment.Also consider specifying
$formatin$wpdb->insert()for explicitness and type safety:♻️ Example with format parameter
- $result = $wpdb->insert( $table_name, $data ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $format = [ '%s', '%s', '%s', '%s', '%s' ]; + $result = $wpdb->insert( $table_name, $data, $format ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Dispatcher.php` around lines 87 - 104, In log_sms() the insert uses a non-existent 'timestamp' column and omits the NOT NULL 'updated_at' column defined in Install.php::create_tables(), causing inserts to fail; replace the 'timestamp' key with 'created_at' and also provide an 'updated_at' value (e.g., same current_time('mysql') value) in the $data array, and call $wpdb->insert( $table_name, $data, $format ) with an appropriate $format array to match column types for explicitness and type safety.
🧹 Nitpick comments (8)
includes/Gateways/Clickatell.php (1)
132-135:reference_idis alwaysnull— Clickatell 202 responses include message IDs.Other gateways extract the provider's message ID from the response body (e.g., Twilio's
sid, Vonage'smessage-id, Plivo'smessage_uuid). For Clickatell, the 202 response body contains message identifiers that could be parsed and returned here for consistent traceability.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Gateways/Clickatell.php` around lines 132 - 135, The current return hardcodes 'reference_id' => null in the Clickatell gateway; update the send/response-handling logic in the Clickatell class to parse the provider response body (typically JSON) when a 202 is received and extract the message identifier (e.g., look for a top-level "messages" array and use messages[0].id or messages[0].messageId, or fallback to any provider-specific id field) and return it as 'reference_id' instead of null; also include a sensible fallback (e.g., check response headers like X-Message-Id or return null only if no id found) so the returned array mirrors other gateways' use of the provider message ID.includes/Gateways/Fake.php (1)
7-11: Nit: Class docblock still says "Twilio Class" — likely a copy-paste leftover./** - * Twilio Class + * Fake Gateway Class * - * `@see` https://www.twilio.com/docs/sms/api/message */🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Gateways/Fake.php` around lines 7 - 11, The class docblock incorrectly labels this as "Twilio Class"—update the docblock above the Fake gateway class (class name Fake) to accurately describe this gateway (e.g., "Fake Gateway" or "Fake SMS gateway") and adjust or remove the Twilio-specific `@see` URL so the comment matches the implementation and intent.includes/Api/Send.php (1)
64-75: Duplicated logging logic — extract to a shared helper.The logging block (lines 69–73) is identical to the one in
includes/Api/Tools.php(lines 64–68). Consider extracting this into a reusable method (e.g., onDispatcheror a base class) to avoid copy-paste drift.♻️ Example: centralize logging in Dispatcher
Add a helper to
Dispatcher:public static function maybe_log_sms( $to, $status ) { if ( ! is_wp_error( $status ) && is_array( $status ) && ! empty( $status['success'] ) ) { $reference_id = isset( $status['reference_id'] ) ? $status['reference_id'] : null; self::log_sms( $to, $reference_id ); } }Then both call sites simplify to:
- // Log the SMS if it was sent successfully - if ( ! is_wp_error( $status ) && is_array( $status ) && isset( $status['success'] ) && $status['success'] ) { - $reference_id = isset( $status['reference_id'] ) ? $status['reference_id'] : null; - Dispatcher::log_sms( $to, $reference_id ); - } + Dispatcher::maybe_log_sms( $to, $status );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Api/Send.php` around lines 64 - 75, Extract the duplicated SMS-success logging into a shared helper on Dispatcher and replace both inline blocks with calls to it: add a public static function maybe_log_sms($to, $status) on class Dispatcher that checks ! is_wp_error($status), is_array($status) and ! empty($status['success']), pulls $reference_id if set, then calls self::log_sms($to, $reference_id); update the call sites in Send.php and Tools.php to call Dispatcher::maybe_log_sms($to, $status) instead of duplicating the conditional logging logic.src/components/HelpResources.js (1)
5-30: Move theresourcesarray outside the component to avoid re-allocating it on every render.The array is pure static data and doesn't depend on props or state. Defining it at module scope avoids the repeated allocation and makes the component body easier to scan.
♻️ Proposed refactor
+const resources = [ + { + id: 'docs', + title: __('Documentation', 'texty'), + // ... + }, + // ... +]; + function HelpResources() { - const resources = [ - { - id: 'docs', - // ... - }, - // ... - ];Note:
__()calls are safe to evaluate at module level in a WordPress JS context — they read from the loaded translation data synchronously.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/HelpResources.js` around lines 5 - 30, The resources array is recreated on each render; move the const resources = [...] definition out of the component function to module scope so it is allocated once (keep the same identifier resources and the existing item keys like id/title/description/url/buttonText/icon), leaving the component to reference resources without changes; keep the __() calls in place (they are safe at module level) and ensure no props/state are referenced inside resources after the move.src/components/VolumeChart.js (1)
35-41: Hardcoded Y-axis domain[0, 10000]with static ticks won't adapt to actual data.For most WordPress plugin installs with low SMS volume, all bars will render near zero height. For high-volume installs (>10,000/month), bars will be clipped. Use Recharts'
'auto'upper bound so the axis scales to the data.♻️ Proposed fix
- <YAxis - domain={[0, 10000]} - ticks={[0, 2500, 5000, 7500, 10000]} - tick={{ fill: '#9ca3af', fontSize: 13 }} - axisLine={false} - tickLine={false} - /> + <YAxis + domain={[0, 'auto']} + tick={{ fill: '#9ca3af', fontSize: 13 }} + axisLine={false} + tickLine={false} + allowDecimals={false} + />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/VolumeChart.js` around lines 35 - 41, The YAxis in VolumeChart.js is hardcoded to domain={[0, 10000]} and static ticks, so it won’t scale to real data; update the YAxis (component name: YAxis in VolumeChart) to use an adaptive upper bound (e.g., domain={[0, 'auto']}) and remove or compute static ticks so Recharts will scale the axis to the data (or compute ticks from the dataset inside the VolumeChart component before rendering) ensuring bars don’t clip or sit near zero.src/app.css (1)
371-377:button-primaryin the quick-send form uses amber (#f59e0b) while the rest of the stylesheet uses green (#16a34a) as the brand color.All other interactive elements (stat card icons, header active links, progress fill, help resource card hover) use the green palette. The amber here appears inconsistent unless it was intentional to visually distinguish the send action.
♻️ Align with brand color if unintentional
.texty-quick-send-form .button-primary { - background-color: `#f59e0b`; - color: white; - border: 1px solid `#f59e0b`; + background-color: `#16a34a`; + color: white; + border: 1px solid `#16a34a`; width: 100%; padding: 10px 16px; } .texty-quick-send-form .button-primary:hover:not(:disabled) { - background-color: `#d97706`; - border-color: `#d97706`; + background-color: `#15803d`; + border-color: `#15803d`; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app.css` around lines 371 - 377, The .texty-quick-send-form .button-primary rule uses amber (`#f59e0b`) which is inconsistent with the project's brand green; update the CSS selector .texty-quick-send-form .button-primary to use the brand green (`#16a34a`) for background-color and border (keep color: white and existing width/padding), so the quick-send button matches other interactive elements like stat card icons/Header active links/progress fill/help card hover.includes/Api.php (1)
27-27:get_volume_chart()issues 12 sequentialSELECT COUNT(*)queries — consolidate into one.The private helper in
includes/Api/Metrics.php(lines ~107–121) executes a separate$wpdb->get_var()per month inside aforeach. Replace with a single aggregation query and merge the results against the expected month list to fill zeros:♻️ Proposed refactor
private function get_volume_chart( $table_name ) { global $wpdb; // Generate expected months first (using corrected strtotime approach) $months = []; $chart_data = []; for ( $i = 11; $i >= 0; $i-- ) { $months[] = gmdate( 'Y-m', strtotime( "-{$i} months", current_time( 'timestamp' ) ) ); } // Single aggregation query $placeholders = implode( ',', array_fill( 0, count( $months ), '%s' ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared $rows = $wpdb->get_results( $wpdb->prepare( "SELECT DATE_FORMAT(timestamp, '%%Y-%%m') AS month, COUNT(*) AS count FROM {$table_name} WHERE DATE_FORMAT(timestamp, '%%Y-%%m') IN ($placeholders) GROUP BY DATE_FORMAT(timestamp, '%%Y-%%m')", ...$months ) ); $counts = []; foreach ( $rows as $row ) { $counts[ $row->month ] = (int) $row->count; } foreach ( $months as $month ) { $chart_data[] = [ 'month' => gmdate( 'M', strtotime( $month . '-01' ) ), 'count' => isset( $counts[ $month ] ) ? $counts[ $month ] : 0, ]; } return $chart_data; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Api.php` at line 27, The get_volume_chart private helper in includes/Api/Metrics.php currently issues one $wpdb->get_var() per month in a loop; replace that with a single aggregated query: first build the 12-month list using gmdate and strtotime with current_time('timestamp'), create a placeholder list for $wpdb->prepare, run one SELECT DATE_FORMAT(timestamp, '%Y-%m') AS month, COUNT(*) AS count FROM {$table_name} WHERE DATE_FORMAT(timestamp, '%Y-%m') IN (...) GROUP BY DATE_FORMAT(timestamp, '%Y-%m'), then map rows into an associative counts array and iterate the original months list to produce chart_data entries with month (gmdate('M', strtotime($month.'-01'))) and count (0 when missing) and return chart_data.includes/Api/Metrics.php (1)
39-46: Silence PHPMD’s unused$requestwarning.If the parameter is intentionally unused, suppress the warning (or rename to
$_request) to keep static analysis clean.🔧 Optional PHPMD suppression
- * `@return` WP_Rest_Response|WP_Error + * `@return` WP_Rest_Response|WP_Error + * `@SuppressWarnings`(PHPMD.UnusedFormalParameter) */ public function get_metrics( $request ) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Api/Metrics.php` around lines 39 - 46, The get_metrics method currently declares an unused parameter $request which triggers PHPMD; fix by either renaming the parameter to $_request (or $_unusedRequest) to signal intentional unusedness, or add a PHPMD suppression annotation on the method (e.g., a `@SuppressWarnings`("PHPMD.UnusedFormalParameter") docblock) so static analysis stops flagging get_metrics($request).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@includes/Api.php`:
- Line 27: The month iteration in Api\Metrics::get_volume_chart() and the
$last_month calculation in get_metrics() use 30*DAY_IN_SECONDS which causes
duplicate/missing months; replace that arithmetic with calendar-month operations
(e.g., use strtotime("-{$i} months", $baseDate) or DateTime::modify("-{$i}
months") and normalize to the first day of each month) so each loop iteration
produces distinct month boundaries; update both get_volume_chart() and
get_metrics() to compute the month label and start/end timestamps via
calendar-aware functions and ensure the loop uses the month index (i) rather
than fixed 30-day offsets.
In `@includes/Api/Metrics.php`:
- Around line 51-52: The current calculation uses a fixed 30‑day offset
(current_time + gmdate with DAY_IN_SECONDS) which can produce incorrect calendar
months; replace that logic for $current_month and $last_month with
calendar-month arithmetic (e.g. use PHP DateTime/DateInterval or strtotime with
"first day of previous month"/"last month" semantics, then format with 'Y-m') so
you always get the previous calendar month, and apply the same change to the
other occurrences around the $... calculations at the 107-111 area; target
symbols: $current_month, $last_month, current_time, gmdate, DAY_IN_SECONDS.
In `@includes/Gateways/Plivo.php`:
- Around line 114-117: The return value currently assigns $body->message_uuid
directly to 'reference_id' but Plivo returns message_uuid as an array, causing
type mismatch for Dispatcher::log_sms(string $reference_id); update the code in
includes/Gateways/Plivo.php (the method that builds this return) to detect if
$body->message_uuid is an array and, if so, extract the first element (or null
if empty), otherwise cast to string; ensure 'reference_id' is always a string or
null before returning so it matches Dispatcher::log_sms's expected type.
In `@includes/Install.php`:
- Around line 40-48: The schema defines created_at and updated_at but
Dispatcher::log_sms inserts a non-existent timestamp column and never supplies
updated_at; update Dispatcher::log_sms to insert into the actual columns
(created_at and updated_at) rather than timestamp, and ensure updated_at gets a
value (e.g., set updated_at = created_at or current time) so the $wpdb->insert()
matches the Install.php table definition; adjust the array keys and values in
Dispatcher::log_sms and verify the columns used in any other $wpdb->insert()
calls reference created_at/updated_at, or alternatively modify Install.php to
add a timestamp column and make updated_at nullable/default CURRENT_TIMESTAMP if
you prefer the schema change.
- Around line 14-21: The installer is calling create_tables() twice and contains
leftover debug error_log() calls; remove the unconditional first call to
create_tables() so that create_tables() is only invoked inside the if (!
$installed) block (which also calls update_option('texty_installed')), and
delete any error_log(...) debug lines related to the installer to avoid noisy
logs (references: create_tables(), update_option('texty_installed'), and
error_log()).
- Line 40: Remove the "IF NOT EXISTS" clause from the CREATE TABLE SQL string so
dbDelta() can correctly parse the table name; locate the SQL assignment that
builds "$sql = \"CREATE TABLE IF NOT EXISTS {$table_name} ( ...\" (referencing
$sql and $table_name) and change it to "CREATE TABLE {$table_name} (...)" before
calling dbDelta() so schema diffs and updates are detected properly by
dbDelta().
In `@src/components/QuickSend.js`:
- Around line 39-42: The catch block in the QuickSend component currently only
resets setIsSending(false) and console.logs the error, so users get no feedback
on network/gateway failures; update the catch handler where apiFetch is called
(inside QuickSend's send flow / sendMessage handler) to call toast.error(...)
with a user-friendly message (e.g., "Failed to send message. Please try again.")
and include the error detail in the log or as part of the toast for debugging,
while still ensuring setIsSending(false) runs; reference the apiFetch call, the
catch callback, setIsSending, and toast.error when making the change.
In `@src/components/StatCard.js`:
- Around line 21-24: Wrap the hardcoded indicator labels in the translation
helper: change the JSX that renders {indicator === 'connected' ? 'Active' :
'Inactive'} inside the StatCard component to use the __('...') function for both
branches so they are localized; update the span rendering where the indicator
variable is used (the texty-stat-badge block) to call __('Active') and
__('Inactive') instead of raw strings and ensure the translation helper is
imported/available in this module if not already.
In `@src/components/VolumeChart.js`:
- Around line 1-9: The project imports Recharts components (BarChart, Bar,
XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer) in VolumeChart.js and
icons from lucide-react in StatCard.js and HelpResources.js but neither
"recharts" nor "lucide-react" are declared in package.json; add both packages to
the dependencies in package.json (choose appropriate semver ranges or latest
stable versions), then run your package manager (npm install or yarn install) to
update lockfile and ensure the imports resolve during build and tests.
In `@src/pages/Dashboard.js`:
- Around line 73-78: The subLabel currently treats any non-positive
metrics.usage_change as "No change", which hides decreases; update the StatCard
subLabel logic in Dashboard.js to handle three cases: positive (prefix with +
and "from last month"), negative (format as e.g. "-X.X% decrease from last
month" or localised equivalent), and zero (keep "No change from last month");
modify the expression that sets subLabel for the StatCard (where title is
"Monthly Usage" and props include value={formatUsage(metrics.monthly_usage)} and
loading={isLoading}) to inspect metrics.usage_change and render the appropriate
signed percentage text for negative values.
---
Duplicate comments:
In `@includes/Api/Tools.php`:
- Around line 64-68: The SMS-success logging block in Tools.php duplicates the
logic in Send.php; replace the inline check and call to Dispatcher::log_sms(...)
with a single call to the centralized helper Dispatcher::maybe_log_sms($to,
$status) (created per the Send.php review), and ensure maybe_log_sms implements
the status validation (is_wp_error, is_array, isset success) and calls
Dispatcher::log_sms($to, $reference_id) internally so this file no longer
repeats the conditional logic.
In `@includes/Dispatcher.php`:
- Around line 87-104: In log_sms() the insert uses a non-existent 'timestamp'
column and omits the NOT NULL 'updated_at' column defined in
Install.php::create_tables(), causing inserts to fail; replace the 'timestamp'
key with 'created_at' and also provide an 'updated_at' value (e.g., same
current_time('mysql') value) in the $data array, and call $wpdb->insert(
$table_name, $data, $format ) with an appropriate $format array to match column
types for explicitness and type safety.
---
Nitpick comments:
In `@includes/Api.php`:
- Line 27: The get_volume_chart private helper in includes/Api/Metrics.php
currently issues one $wpdb->get_var() per month in a loop; replace that with a
single aggregated query: first build the 12-month list using gmdate and
strtotime with current_time('timestamp'), create a placeholder list for
$wpdb->prepare, run one SELECT DATE_FORMAT(timestamp, '%Y-%m') AS month,
COUNT(*) AS count FROM {$table_name} WHERE DATE_FORMAT(timestamp, '%Y-%m') IN
(...) GROUP BY DATE_FORMAT(timestamp, '%Y-%m'), then map rows into an
associative counts array and iterate the original months list to produce
chart_data entries with month (gmdate('M', strtotime($month.'-01'))) and count
(0 when missing) and return chart_data.
In `@includes/Api/Metrics.php`:
- Around line 39-46: The get_metrics method currently declares an unused
parameter $request which triggers PHPMD; fix by either renaming the parameter to
$_request (or $_unusedRequest) to signal intentional unusedness, or add a PHPMD
suppression annotation on the method (e.g., a
`@SuppressWarnings`("PHPMD.UnusedFormalParameter") docblock) so static analysis
stops flagging get_metrics($request).
In `@includes/Api/Send.php`:
- Around line 64-75: Extract the duplicated SMS-success logging into a shared
helper on Dispatcher and replace both inline blocks with calls to it: add a
public static function maybe_log_sms($to, $status) on class Dispatcher that
checks ! is_wp_error($status), is_array($status) and !
empty($status['success']), pulls $reference_id if set, then calls
self::log_sms($to, $reference_id); update the call sites in Send.php and
Tools.php to call Dispatcher::maybe_log_sms($to, $status) instead of duplicating
the conditional logging logic.
In `@includes/Gateways/Clickatell.php`:
- Around line 132-135: The current return hardcodes 'reference_id' => null in
the Clickatell gateway; update the send/response-handling logic in the
Clickatell class to parse the provider response body (typically JSON) when a 202
is received and extract the message identifier (e.g., look for a top-level
"messages" array and use messages[0].id or messages[0].messageId, or fallback to
any provider-specific id field) and return it as 'reference_id' instead of null;
also include a sensible fallback (e.g., check response headers like X-Message-Id
or return null only if no id found) so the returned array mirrors other
gateways' use of the provider message ID.
In `@includes/Gateways/Fake.php`:
- Around line 7-11: The class docblock incorrectly labels this as "Twilio
Class"—update the docblock above the Fake gateway class (class name Fake) to
accurately describe this gateway (e.g., "Fake Gateway" or "Fake SMS gateway")
and adjust or remove the Twilio-specific `@see` URL so the comment matches the
implementation and intent.
In `@src/app.css`:
- Around line 371-377: The .texty-quick-send-form .button-primary rule uses
amber (`#f59e0b`) which is inconsistent with the project's brand green; update the
CSS selector .texty-quick-send-form .button-primary to use the brand green
(`#16a34a`) for background-color and border (keep color: white and existing
width/padding), so the quick-send button matches other interactive elements like
stat card icons/Header active links/progress fill/help card hover.
In `@src/components/HelpResources.js`:
- Around line 5-30: The resources array is recreated on each render; move the
const resources = [...] definition out of the component function to module scope
so it is allocated once (keep the same identifier resources and the existing
item keys like id/title/description/url/buttonText/icon), leaving the component
to reference resources without changes; keep the __() calls in place (they are
safe at module level) and ensure no props/state are referenced inside resources
after the move.
In `@src/components/VolumeChart.js`:
- Around line 35-41: The YAxis in VolumeChart.js is hardcoded to domain={[0,
10000]} and static ticks, so it won’t scale to real data; update the YAxis
(component name: YAxis in VolumeChart) to use an adaptive upper bound (e.g.,
domain={[0, 'auto']}) and remove or compute static ticks so Recharts will scale
the axis to the data (or compute ticks from the dataset inside the VolumeChart
component before rendering) ensuring bars don’t clip or sit near zero.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (20)
includes/Api.phpincludes/Api/Metrics.phpincludes/Api/Send.phpincludes/Api/Tools.phpincludes/Dispatcher.phpincludes/Gateways/Clickatell.phpincludes/Gateways/Fake.phpincludes/Gateways/Plivo.phpincludes/Gateways/Twilio.phpincludes/Gateways/Vonage.phpincludes/Install.phpsrc/App.jssrc/app.csssrc/components/Header.jssrc/components/HelpResources.jssrc/components/QuickSend.jssrc/components/StatCard.jssrc/components/VolumeChart.jssrc/index.jssrc/pages/Dashboard.js
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
includes/Install.php (1)
37-37:⚠️ Potential issue | 🟠 MajorRemove
IF NOT EXISTSfrom thedbDelta()statement.Line 37 still uses
CREATE TABLE IF NOT EXISTS ...;dbDelta()can misparse this and skip schema diffs.🐛 Suggested fix
- $sql = "CREATE TABLE IF NOT EXISTS {$table_name} ( + $sql = "CREATE TABLE {$table_name} (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Install.php` at line 37, The SQL used to create the table is built into the $sql variable and passed to dbDelta(), but it still contains "IF NOT EXISTS" which can cause dbDelta() to misparse and skip schema changes; remove the "IF NOT EXISTS" clause from the CREATE TABLE statement (the string assigned to $sql that uses {$table_name}) so dbDelta() receives a standard CREATE TABLE definition and can correctly apply schema diffs.includes/Api/Metrics.php (1)
52-53:⚠️ Potential issue | 🟠 MajorUse calendar-month arithmetic instead of 30-day offsets.
Line 53 and Line 151 use 30-day subtraction; that can skip or repeat months around month-length boundaries and skew
monthly_usage/volume_chart.🗓️ Suggested fix
- $current_month = current_time( 'Y-m' ); - $last_month = gmdate( 'Y-m', current_time( 'timestamp' ) - 30 * DAY_IN_SECONDS ); + $current = new \DateTimeImmutable( 'first day of this month', wp_timezone() ); + $current_month = $current->format( 'Y-m' ); + $last_month = $current->modify( '-1 month' )->format( 'Y-m' ); ... - for ( $i = 11; $i >= 0; $i-- ) { - $timestamp = current_time( 'timestamp' ) - ( $i * 30 * DAY_IN_SECONDS ); - $months[] = gmdate( 'Y-m', $timestamp ); - } + $current = new \DateTimeImmutable( 'first day of this month', wp_timezone() ); + for ( $i = 11; $i >= 0; $i-- ) { + $months[] = $current->modify( "-{$i} months" )->format( 'Y-m' ); + }Also applies to: 150-152
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Api/Metrics.php` around lines 52 - 53, Replace the 30-day offset arithmetic used to compute $last_month (and the similar code used for monthly_usage/volume_chart) with calendar-month arithmetic: use current_time('timestamp') as the base, create a DateTime/DateTimeImmutable (or use strtotime with relative strings) to compute the first day of the current month and then subtract one calendar month (e.g. "first day of last month") to produce the previous month; format the result with gmdate('Y-m', $timestamp) or DateTime::format and update the variables $current_month and $last_month and the other occurrences so month boundaries use calendar months rather than 30-day offsets.
🧹 Nitpick comments (2)
includes/Install.php (1)
37-45: Add index coverage for metrics read paths.The new metrics queries heavily filter by
created_atandstatus. Without indexes, this table will become a scan hotspot as SMS volume grows.📈 Suggested schema improvement
$sql = "CREATE TABLE {$table_name} ( id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, receiver VARCHAR(20) NOT NULL, gateway VARCHAR(50) NOT NULL, status VARCHAR(20) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, - reference_id VARCHAR(100) DEFAULT NULL + reference_id VARCHAR(100) DEFAULT NULL, + KEY idx_created_at (created_at), + KEY idx_status_created_at (status, created_at) ) {$charset_collate};";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Install.php` around lines 37 - 45, The CREATE TABLE statement built in $sql (in Install.php) lacks indexes for the metrics read paths that filter by created_at and status; add a composite index such as INDEX idx_status_created_at (status, created_at) to the table definition (and optionally a standalone INDEX idx_created_at (created_at) if you expect many range-only queries) so queries filtering/sorting by status and created_at use indexes instead of full table scans.includes/Api/Metrics.php (1)
56-71: AvoidDATE_FORMAT(created_at, ...)filters and per-month query loops.This query shape is index-unfriendly and adds avoidable request latency (12 separate COUNTs). Prefer range predicates and one grouped query for the 12-month series.
Also applies to: 155-162
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Api/Metrics.php` around lines 56 - 71, The current COUNT queries for $monthly_usage and $last_month_usage use DATE_FORMAT(created_at, ...) which is index-unfriendly and repeats per-month queries; replace these by using date range predicates on created_at (e.g., >= month_start AND < next_month_start) in the $wpdb->prepare calls and refactor the 12-month loop into a single grouped query that SELECTs COUNT(*) GROUP BY DATE_FORMAT(created_at, '%Y-%m') or better yet GROUP BY YEAR(created_at), MONTH(created_at) with appropriate range bounds to return the 12-month series in one call; update the code paths that set $monthly_usage, $last_month_usage and the 12-month collection to consume results from that single grouped query and preserve the status = 'sent' filter.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@includes/Dispatcher.php`:
- Line 126: The $wpdb->insert call can fail and is currently ignored; update the
code in Dispatcher.php to check the return value of $wpdb->insert($table_name,
$data) and handle failures explicitly: if the insert returns false, capture
$wpdb->last_error (and/or $wpdb->print_error()), log the error with context
(include $table_name and $data identifiers), and return or surface a failure
(e.g., return false or a WP_Error) so callers know the SMS event wasn't
persisted and metrics can be corrected; ensure you reference the same
$wpdb->insert, $table_name and $data symbols and add no-op behavior only when
insert succeeds.
- Around line 100-112: The current status logic sets $status =
is_wp_error($result) ? 'failed' : 'sent', which misclassifies falsey or explicit
failure responses as 'sent'; update the determination of $status (where $status
and $result are used just before the reference_id extraction block) to mark as
'failed' when is_wp_error($result) OR $result === false OR (is_array($result) &&
isset($result['success']) && !$result['success']) OR empty($result); otherwise
set 'sent', leaving the reference_id extraction code (checking ['sid'],
['message-id'], ['message_uuid']) unchanged.
In `@package.json`:
- Line 25: The dependency "@wedevs/plugin-ui" is referenced as a floating Git
URL; update the package.json entry for "@wedevs/plugin-ui" to pin it to an
immutable revision by appending a tag or commit SHA (e.g.
git+https://github.com/mrabbani/plugin-ui.git#v1.2.3 or #<commit-sha>), then
reinstall/update the lockfile so the lock reflects the pinned revision; ensure
the package name "@wedevs/plugin-ui" is the one you change and commit the
updated package.json and lockfile.
---
Duplicate comments:
In `@includes/Api/Metrics.php`:
- Around line 52-53: Replace the 30-day offset arithmetic used to compute
$last_month (and the similar code used for monthly_usage/volume_chart) with
calendar-month arithmetic: use current_time('timestamp') as the base, create a
DateTime/DateTimeImmutable (or use strtotime with relative strings) to compute
the first day of the current month and then subtract one calendar month (e.g.
"first day of last month") to produce the previous month; format the result with
gmdate('Y-m', $timestamp) or DateTime::format and update the variables
$current_month and $last_month and the other occurrences so month boundaries use
calendar months rather than 30-day offsets.
In `@includes/Install.php`:
- Line 37: The SQL used to create the table is built into the $sql variable and
passed to dbDelta(), but it still contains "IF NOT EXISTS" which can cause
dbDelta() to misparse and skip schema changes; remove the "IF NOT EXISTS" clause
from the CREATE TABLE statement (the string assigned to $sql that uses
{$table_name}) so dbDelta() receives a standard CREATE TABLE definition and can
correctly apply schema diffs.
---
Nitpick comments:
In `@includes/Api/Metrics.php`:
- Around line 56-71: The current COUNT queries for $monthly_usage and
$last_month_usage use DATE_FORMAT(created_at, ...) which is index-unfriendly and
repeats per-month queries; replace these by using date range predicates on
created_at (e.g., >= month_start AND < next_month_start) in the $wpdb->prepare
calls and refactor the 12-month loop into a single grouped query that SELECTs
COUNT(*) GROUP BY DATE_FORMAT(created_at, '%Y-%m') or better yet GROUP BY
YEAR(created_at), MONTH(created_at) with appropriate range bounds to return the
12-month series in one call; update the code paths that set $monthly_usage,
$last_month_usage and the 12-month collection to consume results from that
single grouped query and preserve the status = 'sent' filter.
In `@includes/Install.php`:
- Around line 37-45: The CREATE TABLE statement built in $sql (in Install.php)
lacks indexes for the metrics read paths that filter by created_at and status;
add a composite index such as INDEX idx_status_created_at (status, created_at)
to the table definition (and optionally a standalone INDEX idx_created_at
(created_at) if you expect many range-only queries) so queries filtering/sorting
by status and created_at use indexes instead of full table scans.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/components/QuickSend.js (1)
40-43:⚠️ Potential issue | 🟡 MinorSurface API failures to users in the
catchpath.Line [40]-[43] only resets loading and logs to console; users get no feedback on network/request failures.
🐛 Proposed fix
.catch((err) => { setIsSending(false); - console.log(err); + console.error(err); + toast.error( + err?.message || __('Failed to send message. Please try again.', 'texty') + ); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/QuickSend.js` around lines 40 - 43, The catch block currently only calls setIsSending(false) and console.log(err) which leaves users unaware of failures; update the catch in the QuickSend request path to surface the error to the UI by passing the error message onward — either call an existing error handler prop (e.g., onError(err)) if QuickSend accepts one, or set a component error state (e.g., setErrorMessage(err.message || String(err))) and ensure the component renders that state or triggers a toast/notification; keep the existing setIsSending(false) and include the actual err.message in the user-visible message.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@includes/Api/Metrics.php`:
- Line 59: Replace UTC-based gmdate() usage with site-local DateTimeImmutable
using wp_timezone() so metrics use the same timezone as created_at;
specifically, change the $current_day assignment (and the similar occurrences at
the other two locations) to construct a DateTimeImmutable('now', wp_timezone())
and extract the day via ->format('d') cast to int, ensuring all comparisons
(e.g., DAY(created_at) checks that feed usage_change, delivery_rate, and charts)
use the same timezone basis as Dispatcher.php's current_time('mysql').
In `@src/style.scss`:
- Line 468: Remove the duplicate stylesheet import
"@wedevs/plugin-ui/styles.css" from src/style.scss (the trailing `@import` at the
end of the file) because it violates the no-invalid-position-at-import-rule by
appearing after CSS rules and is redundant with the import already present in
src/index.js; simply delete the lone `@import` "@wedevs/plugin-ui/styles.css" line
at the end of style.scss.
---
Duplicate comments:
In `@src/components/QuickSend.js`:
- Around line 40-43: The catch block currently only calls setIsSending(false)
and console.log(err) which leaves users unaware of failures; update the catch in
the QuickSend request path to surface the error to the UI by passing the error
message onward — either call an existing error handler prop (e.g., onError(err))
if QuickSend accepts one, or set a component error state (e.g.,
setErrorMessage(err.message || String(err))) and ensure the component renders
that state or triggers a toast/notification; keep the existing
setIsSending(false) and include the actual err.message in the user-visible
message.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
includes/Api/Metrics.phpsrc/app.csssrc/components/Header.jssrc/components/HelpResources.jssrc/components/QuickSend.jssrc/components/VolumeChart.jssrc/index.jssrc/pages/Dashboard.jssrc/style.scss
✅ Files skipped from review due to trivial changes (1)
- src/app.css
🚧 Files skipped from review as they are similar to previous changes (3)
- src/components/Header.js
- src/components/VolumeChart.js
- src/components/HelpResources.js
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
includes/Dispatcher.php (2)
116-116:⚠️ Potential issue | 🟠 MajorHandle
$wpdb->insert()failure to avoid silent data loss.Insert failures are silently ignored. If the DB write fails, SMS events are lost and dashboard metrics become inaccurate.
🛠️ Proposed fix
- $wpdb->insert( $table_name, $data ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $inserted = $wpdb->insert( $table_name, $data ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery + if ( false === $inserted ) { + do_action( 'texty_sms_log_insert_failed', $wpdb->last_error, $data ); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Dispatcher.php` at line 116, The $wpdb->insert($table_name, $data) call can fail silently; check its return value and handle failures by inspecting $wpdb->last_error and surface/log the failure so SMS events are not lost. Modify the function containing the $wpdb->insert call to capture the return (e.g. $result = $wpdb->insert(...)); if $result === false, log a descriptive error containing $wpdb->last_error plus context (table name $table_name and encoded $data), and then either return/throw an error or mark the event as failed so callers can handle it (preserve the phpcs ignore comment if needed). Ensure you reference the same $wpdb, $table_name and $data variables when implementing this check.
99-100:⚠️ Potential issue | 🟠 MajorStatus logic can misclassify failed sends as 'sent'.
Non-
WP_Errorfailures (e.g.,false,['success' => false]) will be marked as'sent', skewing delivery metrics.🐛 Proposed fix
// Determine status - $status = is_wp_error( $result ) ? 'failed' : 'sent'; + $status = 'failed'; + if ( ! is_wp_error( $result ) ) { + if ( is_array( $result ) && ! empty( $result['success'] ) ) { + $status = 'sent'; + } elseif ( true === $result ) { + $status = 'sent'; + } + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Dispatcher.php` around lines 99 - 100, The current status assignment uses only is_wp_error($result) which will mark non-WP_Error failures (false, null, or arrays like ['success'=>false]) as 'sent'; update the logic in includes/Dispatcher.php (class Dispatcher, around the $status and $result handling) to treat any falsy result or an explicit failure indicator as 'failed' — e.g., set $status to 'failed' when is_wp_error($result) OR $result === false OR $result === null OR (is_array($result) && array_key_exists('success', $result) && !$result['success']); otherwise set 'sent'. Ensure you reference and update the $status assignment and any downstream code that relies on $status.
🧹 Nitpick comments (2)
includes/Dispatcher.php (1)
118-130: LGTM!The
extract_reference_id()helper cleanly handles multiple gateway response formats. The key list covers common providers (Twilio'ssid, Vonage'smessage-id, Plivo'smessage_uuid, Clickatell'sapiMsgId).Minor style nit: add a blank line after the method signature for consistency with other methods.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Dispatcher.php` around lines 118 - 130, The method extract_reference_id currently has no blank line after its signature; add a single blank line immediately after the function declaration (private function extract_reference_id( $result ) {) so the body starts with an empty line for consistency with the project's method formatting conventions; keep the function name and existing logic unchanged.includes/Install.php (1)
13-22: Minor: Consider adding index for query performance.Once the
dbDelta()call is added, consider adding an index oncreated_atandstatuscolumns sinceMetrics.phpqueries heavily filter by these fields withWHERE created_at >= ... AND status = 'sent'.📊 Schema with index
$sql = "CREATE TABLE {$table_name} ( id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, receiver VARCHAR(20) NOT NULL, gateway VARCHAR(50) NOT NULL, status VARCHAR(20) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, - reference_id VARCHAR(100) DEFAULT NULL + reference_id VARCHAR(100) DEFAULT NULL, + KEY status_created (status, created_at) ) {$charset_collate};";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Install.php` around lines 13 - 22, The table created in create_tables() needs an index on created_at and status to speed up the Metrics.php queries that use WHERE created_at >= ... AND status = 'sent'; update the SQL used in create_tables() (the string passed to dbDelta()) to add a composite index (or separate indexes) on (created_at, status) and then call dbDelta() so WordPress will create the index on existing installs; ensure the index name is unique (e.g., idx_created_at_status) and leave update_option('texty_version', TEXTY_VERSION) intact so migrations can be detected.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@includes/Install.php`:
- Around line 29-46: The create_tables() method builds $sql but never executes
it; update create_tables() so it constructs a dbDelta-compatible CREATE TABLE
statement (remove the "IF NOT EXISTS" from $sql), keep the existing require_once
ABSPATH . 'wp-admin/includes/upgrade.php' and $charset_collate usage, then call
dbDelta($sql) to actually create/update the table; refer to the create_tables()
function and the $sql variable so you remove IF NOT EXISTS and add the
dbDelta($sql) invocation after $sql is set.
---
Duplicate comments:
In `@includes/Dispatcher.php`:
- Line 116: The $wpdb->insert($table_name, $data) call can fail silently; check
its return value and handle failures by inspecting $wpdb->last_error and
surface/log the failure so SMS events are not lost. Modify the function
containing the $wpdb->insert call to capture the return (e.g. $result =
$wpdb->insert(...)); if $result === false, log a descriptive error containing
$wpdb->last_error plus context (table name $table_name and encoded $data), and
then either return/throw an error or mark the event as failed so callers can
handle it (preserve the phpcs ignore comment if needed). Ensure you reference
the same $wpdb, $table_name and $data variables when implementing this check.
- Around line 99-100: The current status assignment uses only
is_wp_error($result) which will mark non-WP_Error failures (false, null, or
arrays like ['success'=>false]) as 'sent'; update the logic in
includes/Dispatcher.php (class Dispatcher, around the $status and $result
handling) to treat any falsy result or an explicit failure indicator as 'failed'
— e.g., set $status to 'failed' when is_wp_error($result) OR $result === false
OR $result === null OR (is_array($result) && array_key_exists('success',
$result) && !$result['success']); otherwise set 'sent'. Ensure you reference and
update the $status assignment and any downstream code that relies on $status.
---
Nitpick comments:
In `@includes/Dispatcher.php`:
- Around line 118-130: The method extract_reference_id currently has no blank
line after its signature; add a single blank line immediately after the function
declaration (private function extract_reference_id( $result ) {) so the body
starts with an empty line for consistency with the project's method formatting
conventions; keep the function name and existing logic unchanged.
In `@includes/Install.php`:
- Around line 13-22: The table created in create_tables() needs an index on
created_at and status to speed up the Metrics.php queries that use WHERE
created_at >= ... AND status = 'sent'; update the SQL used in create_tables()
(the string passed to dbDelta()) to add a composite index (or separate indexes)
on (created_at, status) and then call dbDelta() so WordPress will create the
index on existing installs; ensure the index name is unique (e.g.,
idx_created_at_status) and leave update_option('texty_version', TEXTY_VERSION)
intact so migrations can be detected.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
includes/Api/Metrics.phpincludes/Dispatcher.phpincludes/Install.phpsrc/style.scss
✅ Files skipped from review due to trivial changes (1)
- src/style.scss
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
includes/Install.php (1)
37-45:⚠️ Potential issue | 🟠 MajorRemove
IF NOT EXISTSfrom thedbDelta()DDL.
dbDelta()can mis-parseCREATE TABLE IF NOT EXISTS ..., which can prevent future schema diffs from applying reliably.Suggested fix
- $sql = "CREATE TABLE IF NOT EXISTS {$table_name} ( + $sql = "CREATE TABLE {$table_name} ( id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, receiver VARCHAR(20) NOT NULL, gateway VARCHAR(50) NOT NULL, status VARCHAR(20) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, reference_id VARCHAR(100) DEFAULT NULL ) {$charset_collate};";WordPress dbDelta CREATE TABLE syntax requirements: is "IF NOT EXISTS" recommended or known to break dbDelta table parsing/diff detection?🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Install.php` around lines 37 - 45, The CREATE TABLE statement assigned to $sql in includes/Install.php uses "CREATE TABLE IF NOT EXISTS", which breaks WordPress dbDelta() parsing; remove the "IF NOT EXISTS" token so the SQL reads "CREATE TABLE {$table_name} (... ) {$charset_collate};" (leave all columns, $table_name and $charset_collate intact) before calling dbDelta() to ensure future schema diffs are detected correctly.
🧹 Nitpick comments (1)
includes/Install.php (1)
38-45: Add indexes for metrics query paths.Current schema lacks indexes aligned with dashboard reads (
status,created_atfilters). This will degrade astexty_sms_statgrows.Suggested schema indexing
$sql = "CREATE TABLE {$table_name} ( id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, receiver VARCHAR(20) NOT NULL, gateway VARCHAR(50) NOT NULL, status VARCHAR(20) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, - reference_id VARCHAR(100) DEFAULT NULL + reference_id VARCHAR(100) DEFAULT NULL, + KEY idx_created_at (created_at), + KEY idx_status_created_at (status, created_at) ) {$charset_collate};";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/Install.php` around lines 38 - 45, Add appropriate indexes to the texty_sms_stat table to support dashboard queries that filter by status and created_at: alter the CREATE TABLE statement in includes/Install.php (the block defining texty_sms_stat with columns id, receiver, gateway, status, created_at, updated_at, reference_id) to include an index on status and a composite index on (created_at, status) (or created_at alone if preferred) so reads for metrics/dashboards are efficient; update the schema string accordingly and ensure index names are unique (e.g., idx_texty_status, idx_texty_created_at_status).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@includes/Api/Metrics.php`:
- Line 196: Replace the DateTimeImmutable->format('M') usage with a call to
wp_date so month labels are localized; specifically, where the array sets
'month' (using new \DateTimeImmutable($month_key . '-01', wp_timezone())), pass
that date's timestamp (or the equivalent strtotime of "$month_key-01") into
wp_date('M', ...) with wp_timezone() so WordPress returns translated month
abbreviations for the dashboard output.
---
Duplicate comments:
In `@includes/Install.php`:
- Around line 37-45: The CREATE TABLE statement assigned to $sql in
includes/Install.php uses "CREATE TABLE IF NOT EXISTS", which breaks WordPress
dbDelta() parsing; remove the "IF NOT EXISTS" token so the SQL reads "CREATE
TABLE {$table_name} (... ) {$charset_collate};" (leave all columns, $table_name
and $charset_collate intact) before calling dbDelta() to ensure future schema
diffs are detected correctly.
---
Nitpick comments:
In `@includes/Install.php`:
- Around line 38-45: Add appropriate indexes to the texty_sms_stat table to
support dashboard queries that filter by status and created_at: alter the CREATE
TABLE statement in includes/Install.php (the block defining texty_sms_stat with
columns id, receiver, gateway, status, created_at, updated_at, reference_id) to
include an index on status and a composite index on (created_at, status) (or
created_at alone if preferred) so reads for metrics/dashboards are efficient;
update the schema string accordingly and ensure index names are unique (e.g.,
idx_texty_status, idx_texty_created_at_status).
* feat(dashboard): add default dashboard with metrics, chart and quick send * feat(dashboard): add default dashboard with metrics, chart and quick send * feat(dashboard): add default dashboard with metrics, chart and quick send * feat(dashboard): add default dashboard with metrics, chart and quick send * feat(dashboard): add default dashboard with metrics, chart and quick send * Composer.lock file added * feat(settings): migrate settings page to plugin-ui schema-driven components Replace the hand-rolled React settings UI with the schema-driven <Settings> component from @wedevs/plugin-ui, backed by BaseSettingsRESTController from wedevs/wp-kit. Preserves full backward compatibility with the existing texty_settings wp_option structure and gateway credential validation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(settings): add filter for third-party gateways and order prop for sorting - Add `texty_settings_schema` filter in get_settings_schema() to allow third-party plugins to register gateway sections and fields - Add `order()` method to GatewayInterface (default 10, Fake returns 99) - Add `priority` prop to all gateway sections in schema - Sort gateway dropdown options by order, then alphabetically Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: make order() optional, only override in Fake gateway Remove order() from GatewayInterface and default-10 gateways. Only Fake overrides it (returns 99). get_gateway_options() already falls back to 10 via method_exists() check. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(settings): set gateway_selection section priority to 9 Ensures the gateway selector section always appears above gateway credential sections (priority 10) regardless of sort order. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Feat/revemp design layout (#75) * feat(menu): enhance admin menu registration with capability checks and dynamic submenus * feat(settings): update package dependencies and add TypeScript configuration * feat: integrate Tailwind CSS and update styles - Added Tailwind CSS as a dependency and configured PostCSS. - Created a new base Tailwind CSS file for global styles. - Removed legacy CSS styles from app.css and migrated to Tailwind utility classes. - Updated Header component to utilize Tailwind for layout and styling. - Adjusted index.js to import the new base Tailwind CSS file instead of plugin UI styles. - Refactored SCSS styles to use Tailwind utilities and Sass color functions for better maintainability. - Updated package.json to include new dependencies for Tailwind and PostCSS. * feat: migrate to React Router v7 and refactor App structure * refactor: update package dependencies and improve header component - Changed dependency for @wedevs/plugin-ui from GitHub to local file reference. - Enhanced base-tailwind.css with improved import statements and added button styles. - Refactored Header component to utilize new icons and dropdown menu for help items. - Updated TypeScript definitions to include support_url and feature_request_url. - Added watch options in webpack.config.js to ignore dist folder during rebuilds. * Add frontend development guidelines and update project documentation - Introduced SKILL.md for frontend development rules and conventions, covering React, TypeScript, Tailwind, and more. - Created CLAUDE.md to provide guidance for working with the Texty SMS notification plugin, detailing architecture, common commands, and feature addition processes. - Updated .gitignore to exclude seed files. * refactor: restructure dashboard components and remove unused files - Deleted HelpResources, StatCard, VolumeChart, and Dashboard components. - Introduced QuickSendCard and StatCards components for better modularity. - Updated Dashboard to utilize new components and improved data fetching logic. - Enhanced VolumeAnalytics for better data visualization. - Refactored routing to point to new dashboard structure. - Updated type definitions for better TypeScript support. - Adjusted imports to use the new plugin UI components. * docs: update code review and backend development guidelines to enforce class import standards * feat: enhance layout components and improve routing structure with new hooks and skeletons Co-authored-by: Copilot <copilot@github.com> * feat: add backButtonLabel prop to layout components and update routing documentation * feat: add error handling components with ErrorBoundary and NotFound pages Co-authored-by: Copilot <copilot@github.com> * feat: add ErrorBoundary and NotFound components for improved error handling * feat: update verification commands guidance for frontend edits and clarify user-triggered actions * feat: bridge plugin-ui theme tokens into Tailwind utility names for improved styling * feat: update submenu item identifiers to use 'path' instead of 'id' for improved clarity * feat: Update PHPCS configuration and enhance Tailwind CSS styles for improved layout consistency Co-authored-by: Copilot <copilot@github.com> * feat: Introduce GatewayStatus component and update dashboard to utilize new status display * feat: Add PhoneField component and AppLayout for enhanced form functionality * Update AppLayout.tsx * feat: Implement dashboard components and hooks for enhanced user experience * Use @wordpress/element for runtime React imports * feat: Enhance routing with lazy loading and suspense for improved performance * feat: Remove unused toastify styles to streamline CSS * Defensive handling of null recipients/values * feat: update API class registration and enhance version handling in Header component * feat(Menu): update submenu path handling to support external URLs --------- Co-authored-by: Copilot <copilot@github.com> --------- Co-authored-by: SHAMIM <111671483+Shamim-97@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Kamruzzaman <kzamanbn@gmail.com> Co-authored-by: Copilot <copilot@github.com>
* feat(dashboard): add default dashboard with metrics, chart and quick send * feat(dashboard): add default dashboard with metrics, chart and quick send * feat(dashboard): add default dashboard with metrics, chart and quick send * feat(dashboard): add default dashboard with metrics, chart and quick send * feat(dashboard): add default dashboard with metrics, chart and quick send * Composer.lock file added * feat(settings): migrate settings page to plugin-ui schema-driven components Replace the hand-rolled React settings UI with the schema-driven <Settings> component from @wedevs/plugin-ui, backed by BaseSettingsRESTController from wedevs/wp-kit. Preserves full backward compatibility with the existing texty_settings wp_option structure and gateway credential validation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(settings): add filter for third-party gateways and order prop for sorting - Add `texty_settings_schema` filter in get_settings_schema() to allow third-party plugins to register gateway sections and fields - Add `order()` method to GatewayInterface (default 10, Fake returns 99) - Add `priority` prop to all gateway sections in schema - Sort gateway dropdown options by order, then alphabetically Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: make order() optional, only override in Fake gateway Remove order() from GatewayInterface and default-10 gateways. Only Fake overrides it (returns 99). get_gateway_options() already falls back to 10 via method_exists() check. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(settings): set gateway_selection section priority to 9 Ensures the gateway selector section always appears above gateway credential sections (priority 10) regardless of sort order. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(menu): enhance admin menu registration with capability checks and dynamic submenus * feat(settings): update package dependencies and add TypeScript configuration * feat: integrate Tailwind CSS and update styles - Added Tailwind CSS as a dependency and configured PostCSS. - Created a new base Tailwind CSS file for global styles. - Removed legacy CSS styles from app.css and migrated to Tailwind utility classes. - Updated Header component to utilize Tailwind for layout and styling. - Adjusted index.js to import the new base Tailwind CSS file instead of plugin UI styles. - Refactored SCSS styles to use Tailwind utilities and Sass color functions for better maintainability. - Updated package.json to include new dependencies for Tailwind and PostCSS. * feat: migrate to React Router v7 and refactor App structure * refactor: update package dependencies and improve header component - Changed dependency for @wedevs/plugin-ui from GitHub to local file reference. - Enhanced base-tailwind.css with improved import statements and added button styles. - Refactored Header component to utilize new icons and dropdown menu for help items. - Updated TypeScript definitions to include support_url and feature_request_url. - Added watch options in webpack.config.js to ignore dist folder during rebuilds. * Add frontend development guidelines and update project documentation - Introduced SKILL.md for frontend development rules and conventions, covering React, TypeScript, Tailwind, and more. - Created CLAUDE.md to provide guidance for working with the Texty SMS notification plugin, detailing architecture, common commands, and feature addition processes. - Updated .gitignore to exclude seed files. * refactor: restructure dashboard components and remove unused files - Deleted HelpResources, StatCard, VolumeChart, and Dashboard components. - Introduced QuickSendCard and StatCards components for better modularity. - Updated Dashboard to utilize new components and improved data fetching logic. - Enhanced VolumeAnalytics for better data visualization. - Refactored routing to point to new dashboard structure. - Updated type definitions for better TypeScript support. - Adjusted imports to use the new plugin UI components. * docs: update code review and backend development guidelines to enforce class import standards * feat: enhance layout components and improve routing structure with new hooks and skeletons Co-authored-by: Copilot <copilot@github.com> * feat: add backButtonLabel prop to layout components and update routing documentation * feat: add error handling components with ErrorBoundary and NotFound pages Co-authored-by: Copilot <copilot@github.com> * feat: add ErrorBoundary and NotFound components for improved error handling * feat: update verification commands guidance for frontend edits and clarify user-triggered actions * feat: bridge plugin-ui theme tokens into Tailwind utility names for improved styling * feat: update submenu item identifiers to use 'path' instead of 'id' for improved clarity * feat: Update PHPCS configuration and enhance Tailwind CSS styles for improved layout consistency Co-authored-by: Copilot <copilot@github.com> * feat: Introduce GatewayStatus component and update dashboard to utilize new status display * feat: Add PhoneField component and AppLayout for enhanced form functionality * Update AppLayout.tsx * Refactor gateway settings management - Removed the old SettingsPage component and replaced it with a new GatewayConfiguration component that handles gateway settings. - Introduced new components for the gateway detail pane, sidebar, header, and section to improve modularity and readability. - Implemented a loading skeleton for better user experience during data fetching. - Updated routing to reflect the new structure, replacing the old settings page with the new gateway configuration page. * feat: Implement dashboard components and hooks for enhanced user experience * Use @wordpress/element for runtime React imports * Use @wordpress/element for runtime React imports * feat: Enhance routing with lazy loading and suspense for improved performance * feat: Remove unused toastify styles to streamline CSS * Defensive handling of null recipients/values * feat: Notifications management UI (user events, integrations, settings) (#77) * feat: Implement notifications management with user events, integrations, and settings components * Use @wordpress/element for runtime React imports * Feature notifications settings (#79) * feature notifications settings * feature notifications settings * remove STOP settings * STOP feature remove * Feat/logs (#78) * feat: add SMS logs feature with REST API and UI components - Introduced a new Logs API endpoint for fetching SMS logs. - Added Logs class to handle REST routes and data retrieval. - Created UI components for displaying logs, including LogDetailDialog and StatusBadge. - Implemented hooks for managing logs data and filters. - Updated database schema to include notification context, message, and response fields. - Registered new routes in the application for accessing logs. - Enhanced SmsStat model to support new fields and methods for logs. - Added migration scripts for database updates related to logs. * Add migration notices and REST endpoints Wire up wp-kit migration + admin-notice integration: register MigrationRESTController and NoticeRESTController under texty/v1, add a NoticeProvider (includes AJAX handler 'texty_run_migration') that surfaces a non-dismissible "Update database" admin notice, and register MigrationHooks to keep db_version in sync. Add a Notices React component and render it in AppLayout; expose rest_url, ajax_url and nonce in the localized script and update the Texty global TS types. Instantiate the migrations bootstrap during plugin init via a migrations() accessor (migrations are not auto-applied on boot; users must click the admin notice to trigger the upgrade). Error handling/logging retained for upgrade failures. * feat: update default logs perPage to 30 for improved data display * feat: log full notification context to SmsStat on send Every `texty_after_send_sms` event now writes the new columns added in the 2.0.0 migration (notification_id, notification_group, message, response) so live rows match the structure the SMS Logs UI and the seeder produce. - Notifications registry gains a small set_active/get_active/clear_active stash. Notification::send and WC\\Base::send wrap their gateway send loop in set_active() + try/finally + clear_active() so the after-send hook can read which notification fired the SMS without threading context through the gateway pipeline. - Dispatcher::log_sms reads the active notification, pulls get_id() / get_group(), and persists them alongside the final message body. The response column gets a normalized shape via encode_response(): WP_Error is unpacked into {code, message, data}; arrays/objects pass through. - Plivo's message_uuid (which the SDK returns as ["uuid"]) is now flattened to its first element so reference_id stays scalar. - log_sms bails early if DataLayerFactory::make_store returns null, preventing a fatal when the store isn't initialized. * refactor(migrations): delegate V_2_0_0 schema sync to Install::create_tables * feat(Menu): update submenu path handling to support external URLs * feat: update API class registration and enhance version handling in Header component * fix: restore parent signature compat for WC\Base::send() Notification::send() declares ': bool' return type, but WC\Base overrode it with no return type and bare 'return;' on early exits, producing a fatal at autoload time. Align the override to ': bool' and return true/false in each path. Also restore a missing '/**' docblock opener above migrations() in texty.php that slipped through the merge resolution and caused a parse error. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: SHAMIM <111671483+Shamim-97@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: SHAMIM <111671483+Shamim-97@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: SHAMIM <111671483+Shamim-97@users.noreply.github.com> Co-authored-by: Md Mahbub Rabbani <mahbub.rucse@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Copilot <copilot@github.com>
- Changed the dependency for @wedevs/plugin-ui from a local file reference to a GitHub repository. - Removed the pot2json script from package.json as it is no longer needed. - Cleaned up the dependencies by removing the po2json package.
* feat: add Playwright end-to-end testing setup with configuration and initial tests * feat: add TypeScript configuration for tests and update dependencies * refactor: remove old notifications, settings, tools, and settings page components * Add Playwright e2e tests for Texty admin Add a suite of Playwright end-to-end tests for the Texty admin SPA and REST API: dashboard, gateway, logs, notifications, routing, and REST API smoke tests. Introduce test helpers (tests/e2e/helpers/texty.ts) for navigating hash routes, waiting for API responses and obtaining REST nonces. Update playwright.config.ts to disable fullyParallel, set workers to 2 and enable ignoreHTTPSErrors to improve stability in CI/local runs. Improve tests/e2e/texty-admin.spec.ts to capture console/page errors during SPA mount and provide clearer failure logging. * style: improve CSS resets and clean up LogDetailDialog component * ci: skip deleted and non-PHP files in phpcs workflow Filter PR file list to existing *.php files before invoking phpcs. Prevents "file does not exist" errors when a PR deletes files and the resulting empty checkstyle output breaks cs2pr XML parsing.
…plugin dependency
…GroupSettings component
…d SMS logs, expanded REST API, and compliance controls
…ast notifications and notification settings schema
… phone number handling in UI components
…in gateway classes; enhance log date formatting in UI
get_items() called $item->get_product()->get_name() directly, but get_product() returns false for line items whose product has since been deleted, throwing "Call to a member function get_name() on false" and aborting the woocommerce_order_status_changed hook — so no order SMS (admin or customer) was sent for affected orders. Fall back to the line-item name stored on the order when the product is unavailable.
Logs::get_item() passed an int to BaseDataStore::read(), whose signature is read( ModelInterface &$model ) — a TypeError fataling (HTTP 500) on every single-log request. read() also throws on not-found rather than returning false, so the intended 404 branch was unreachable. Query by the id column instead: it returns the same raw row shape present_row() already consumes for the listing, and an empty set (not an exception) on not-found, restoring the 404 path.
Texty 2.0 — Complete revamp
Ground-up revamp of Texty: the entire admin is redesigned and rebuilt as a modern React 18 + TypeScript SPA, plus a new logging/metrics backend, schema-driven settings, and a migration framework.
Highlights
@wedevs/plugin-ui, Tailwind v4. Legacysrc/*.jsscreens removed.{prefix}texty_sms_stattable (DataLayerSmsStatmodel).REST API (
texty/v1)New / expanded endpoints:
metrics,logs,gateway,settings/schema,notification-settings,notifications/schema.Dev / tooling
bin/version-replace.sh— stamps@since TEXTY_VERSIONfrompackage.jsonat build (npm run version), wired intobin/build.sh.CLAUDE.md+ contributor skill guides; updated build / webpack / tsconfig pipeline.Changelog
Full changelog in
CHANGELOG.md.Version
Major release:
1.1.5 → 2.0.0(bump pending inpackage.json,texty.php,readme.txt).Test plan
texty_sms_statcolumns/indexes creatednpm run typecheck,npm run build,composer phpcscleanStats
120 files changed, +14.4k / −2.3k.
Summary by CodeRabbit