Skip to content

feat: Texty 2.0 revamp with new features#57

Merged
kzamanbd merged 36 commits into
developfrom
feature/dashboard
Jun 16, 2026
Merged

feat: Texty 2.0 revamp with new features#57
kzamanbd merged 36 commits into
developfrom
feature/dashboard

Conversation

@Shamim-97

@Shamim-97 Shamim-97 commented Feb 24, 2026

Copy link
Copy Markdown
Contributor

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.

  • Closes getdokan/texty-pro#14 · Roadmap: getdokan/plugin-internal-tasks#1872

Highlights

  • Full design revamp — every admin screen redesigned and rebuilt from scratch (new layout, navigation, theming, component system).
  • Admin rewrite — React 18 + TypeScript SPA with hash routing, @wedevs/plugin-ui, Tailwind v4. Legacy src/*.js screens removed.
  • Dashboard — SMS metrics, delivery-rate stats, volume analytics chart, Quick Send card.
  • SMS Logs — searchable / filterable / paginated log with detail view and status badges, backed by a new {prefix}texty_sms_stat table (DataLayer SmsStat model).
  • Gateway configuration — schema-driven settings UI; Connect → Activate flow; per-gateway Activate / Deactivate / Disconnect.
  • Notifications revamp — User Events / Integrations / Settings tabs; collapsible per-event rows (toggle, recipients, message, variables); per-group branding (WordPress / WooCommerce / Dokan); schema generated server-side.
  • Compliance — Pause All, append company name, global fallback Sender ID.
  • Migrations — wp-kit migration framework + "database update required" admin notice; 2.0.0 migration adds SMS Logs columns/indexes.

REST API (texty/v1)

New / expanded endpoints: metrics, logs, gateway, settings/schema, notification-settings, notifications/schema.

Dev / tooling

  • Playwright e2e suite (dashboard, gateway, logs, notifications, routing, REST).
  • bin/version-replace.sh — stamps @since TEXTY_VERSION from package.json at build (npm run version), wired into bin/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 in package.json, texty.php, readme.txt).

Test plan

  • Dashboard loads — metrics, chart, quick send
  • Gateway connect / activate / deactivate / disconnect
  • Notifications — toggle, recipients, message edit; save persists across reload (wp + wc/dokan)
  • SMS Logs — list, filter, search, paginate, detail view
  • DB migration notice fires on upgrade; texty_sms_stat columns/indexes created
  • npm run typecheck, npm run build, composer phpcs clean

Stats

120 files changed, +14.4k / −2.3k.

Summary by CodeRabbit

  • New Features
    • Dashboard page with stats, charts, quick-send UI and help resources; new metrics API providing gateway status, usage, delivery rate and volume chart.
  • Chores
    • Installer now creates a table to record SMS send activity.
  • New Behavior
    • SMS sends are logged (sent/failed) with optional reference IDs.
  • Refactor
    • Gateways now return structured success responses including reference IDs; UI theme and styling updates for the dashboard.

@coderabbitai

coderabbitai Bot commented Feb 24, 2026

Copy link
Copy Markdown

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Registers a Metrics REST controller and dashboard; changes gateway send() success returns to structured arrays; logs SMS send results into a new texty_sms_stat table; adds dashboard UI components, styles, theme provider, and related frontend dependencies.

Changes

Cohort / File(s) Summary
API: Registration & Controller
includes/Api.php, includes/Api/Metrics.php
Adds Api\Metrics to API registration and implements /texty/v1/metrics REST controller that aggregates gateway status, current/last-month usage, 30-day delivery rate, and 12-month volume data.
SMS Logging & Install
includes/Dispatcher.php, includes/Tools.php, includes/Install.php
Adds Dispatcher::log_sms() and extract_reference_id(); Hooks texty_after_send_sms and calls logging from Tools for successful sends; Install now creates texty_sms_stat table with receiver, gateway, status, timestamps, and reference_id.
Gateways: send() Return Shape
includes/Gateways/Clickatell.php, includes/Gateways/Fake.php, includes/Gateways/Plivo.php, includes/Gateways/Twilio.php, includes/Gateways/Vonage.php
All gateway send() implementations/docblocks changed to return `WP_Error
Frontend: Dashboard, Routing, Theme
src/pages/Dashboard.js, src/App.js, src/index.js, package.json
Adds Dashboard page fetching /texty/v1/metrics, updates routing to /dashboard, wraps app in ThemeProvider with new tokens, and adds dependencies (lucide-react, recharts, @wedevs/plugin-ui).
Frontend: Components & UI
src/components/StatCard.js, src/components/VolumeChart.js, src/components/HelpResources.js, src/components/Header.js, src/components/QuickSend.js
Introduces StatCard, VolumeChart, HelpResources; updates Header navigation; QuickSend migrated to @wedevs/plugin-ui components and clears inputs on successful send.
Styling
src/app.css, src/style.scss
Adds comprehensive dashboard CSS (app.css) and small SCSS EOF formatting change.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I hopped through logs and rows so fine,
I chased each send and saved its sign,
Charts now bloom where numbers play,
Reference IDs tucked away,
A rabbit cheers — metrics align!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 69.23% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is vague and overly broad, using generic phrasing like 'revamp' and 'new features' without specifying the main changes. It lacks concrete detail about what was actually implemented. Consider a more specific title that highlights the primary changes, such as 'feat: Add metrics dashboard and SMS logging system' or 'feat: Implement Metrics API with dashboard UI'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/dashboard

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

♻️ Duplicate comments (2)
includes/Api/Tools.php (1)

64-68: Duplicate logging logic — same as Send.php.

This block is identical to the one in includes/Api/Send.php (lines 69–73). See the refactoring suggestion in the Send.php review to centralize this into Dispatcher::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 | 🔴 Critical

Column name timestamp does not exist in the table schema — see related comment on Install.php.

The insert on line 97 references a timestamp column, but Install.php::create_tables() defines created_at and updated_at instead. Additionally, updated_at is NOT NULL with no default and is never provided. This will cause every $wpdb->insert() call to fail. See the detailed fix options in the Install.php review comment.

Also consider specifying $format in $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_id is always null — Clickatell 202 responses include message IDs.

Other gateways extract the provider's message ID from the response body (e.g., Twilio's sid, Vonage's message-id, Plivo's message_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., on Dispatcher or 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 the resources array 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-primary in 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 sequential SELECT 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 a foreach. 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 $request warning.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 258463a and 0d320fa.

📒 Files selected for processing (20)
  • includes/Api.php
  • includes/Api/Metrics.php
  • includes/Api/Send.php
  • includes/Api/Tools.php
  • includes/Dispatcher.php
  • includes/Gateways/Clickatell.php
  • includes/Gateways/Fake.php
  • includes/Gateways/Plivo.php
  • includes/Gateways/Twilio.php
  • includes/Gateways/Vonage.php
  • includes/Install.php
  • src/App.js
  • src/app.css
  • src/components/Header.js
  • src/components/HelpResources.js
  • src/components/QuickSend.js
  • src/components/StatCard.js
  • src/components/VolumeChart.js
  • src/index.js
  • src/pages/Dashboard.js

Comment thread includes/Api.php
Comment thread includes/Api/Metrics.php Outdated
Comment thread includes/Gateways/Plivo.php
Comment thread includes/Install.php Outdated
Comment thread includes/Install.php
Comment thread includes/Install.php
Comment thread src/components/QuickSend.js
Comment thread src/components/StatCard.js Outdated
Comment thread src/components/VolumeChart.js Outdated
Comment thread src/pages/Dashboard.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
includes/Install.php (1)

37-37: ⚠️ Potential issue | 🟠 Major

Remove IF NOT EXISTS from the dbDelta() 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 | 🟠 Major

Use 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_at and status. 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: Avoid DATE_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.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0d320fa and 253872a.

📒 Files selected for processing (4)
  • includes/Api/Metrics.php
  • includes/Dispatcher.php
  • includes/Install.php
  • package.json

Comment thread includes/Dispatcher.php Outdated
Comment thread includes/Dispatcher.php Outdated
Comment thread package.json Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/components/QuickSend.js (1)

40-43: ⚠️ Potential issue | 🟡 Minor

Surface API failures to users in the catch path.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 253872a and 9a61633.

📒 Files selected for processing (9)
  • includes/Api/Metrics.php
  • src/app.css
  • src/components/Header.js
  • src/components/HelpResources.js
  • src/components/QuickSend.js
  • src/components/VolumeChart.js
  • src/index.js
  • src/pages/Dashboard.js
  • src/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

Comment thread includes/Api/Metrics.php Outdated
Comment thread src/style.scss Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
includes/Dispatcher.php (2)

116-116: ⚠️ Potential issue | 🟠 Major

Handle $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 | 🟠 Major

Status logic can misclassify failed sends as 'sent'.

Non-WP_Error failures (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's sid, Vonage's message-id, Plivo's message_uuid, Clickatell's apiMsgId).

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 on created_at and status columns since Metrics.php queries heavily filter by these fields with WHERE 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

📥 Commits

Reviewing files that changed from the base of the PR and between 9a61633 and 2438b3f.

📒 Files selected for processing (4)
  • includes/Api/Metrics.php
  • includes/Dispatcher.php
  • includes/Install.php
  • src/style.scss
✅ Files skipped from review due to trivial changes (1)
  • src/style.scss

Comment thread includes/Install.php Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
includes/Install.php (1)

37-45: ⚠️ Potential issue | 🟠 Major

Remove IF NOT EXISTS from the dbDelta() DDL.

dbDelta() can mis-parse CREATE 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_at filters). This will degrade as texty_sms_stat grows.

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).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2438b3f and e38e5c5.

📒 Files selected for processing (2)
  • includes/Api/Metrics.php
  • includes/Install.php

Comment thread includes/Api/Metrics.php Outdated
@Shamim-97 Shamim-97 self-assigned this Feb 26, 2026
@Shamim-97 Shamim-97 added the invalid This doesn't seem right label Mar 3, 2026
* 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>
@kzamanbd kzamanbd added Needs Testing and removed invalid This doesn't seem right labels May 6, 2026
kzamanbd and others added 2 commits May 6, 2026 17:49
* 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>
@kzamanbd kzamanbd changed the title Feature/dashboard feat: Texty 2.0 revamp with new features May 8, 2026
@kzamanbd kzamanbd assigned kzamanbd and unassigned Shamim-97 May 8, 2026
kzamanbd added 4 commits May 8, 2026 10:13
- 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.
Copilot stopped work on behalf of kzamanbd due to an error June 11, 2026 11:38
Copilot AI requested a review from kzamanbd June 11, 2026 11:38
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.
@kzamanbd kzamanbd merged commit 8a01678 into develop Jun 16, 2026
1 check failed
@kzamanbd kzamanbd deleted the feature/dashboard branch June 16, 2026 10:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants