Plugin Directory

Changeset 3712213


Ignore:
Timestamp:
09/25/2026 01:17:17 AM (4 weeks ago)
Author:
hamzaafridi05
Message:

1.2.0

New: start questions. The chat can open with "Are you already a customer?". Customers give their order number and email, new visitors give their email, before they type. Turn it on in Settings, Appearance.
New: with WooCommerce, the order number is checked against your orders. Only the store owner sees the result, in the Inbox.
New: the Inbox search also finds emails and order numbers.
Fix: saving a visitor's email no longer moves a live chat out of the live inbox.
Fix: the privacy eraser removes every chat for an email, and export and erase find an email saved from the chat.
Fix: the privacy consent note did not show, even when it was turned on.
Fix: opening a chat from its email link stopped the chat from loading.

Location:
sellsmith
Files:
27 added
14 edited

Legend:

Unmodified
Added
Removed
  • sellsmith/trunk/admin/class-sellsmith-admin.php

    r3587090 r3712213  
    745745                <?php
    746746                self::panel_close();
     747
     748                self::panel_open( __( 'When the chat opens', 'sellsmith' ) );
     749                ?>
     750                        <?php
     751                        self::checkbox_row(
     752                                'start_step',
     753                                __( 'Start questions', 'sellsmith' ),
     754                                __( 'Ask "Are you already a customer?" first. Customers give their order number and email, new visitors give their email. Then they type their question.', 'sellsmith' )
     755                        );
     756                        ?>
     757                        <div class="ss-field">
     758                                <span class="ss-label"><?php esc_html_e( 'Email', 'sellsmith' ); ?></span>
     759                                <div class="ss-radio-group">
     760                                        <label class="ss-radio"><input type="radio" name="<?php echo self::field_name( 'start_email' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" value="required" <?php checked( 'required', Sellsmith_Settings::get( 'start_email' ) ); ?> /> <?php esc_html_e( 'Required, so you can always reply', 'sellsmith' ); ?></label>
     761                                        <label class="ss-radio"><input type="radio" name="<?php echo self::field_name( 'start_email' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" value="optional" <?php checked( 'optional', Sellsmith_Settings::get( 'start_email' ) ); ?> /> <?php esc_html_e( 'Optional, visitors can skip it', 'sellsmith' ); ?></label>
     762                                </div>
     763                        </div>
     764                        <?php
     765                        self::text_row( 'start_question', __( 'Question', 'sellsmith' ) );
     766                        self::text_row( 'start_yes', __( 'Button for customers', 'sellsmith' ) );
     767                        self::text_row( 'start_no', __( 'Button for new visitors', 'sellsmith' ) );
     768                        ?>
     769                        <p class="ss-hint"><?php esc_html_e( 'With WooCommerce, the order number is checked against your orders. Only you see the result, in the Inbox. The visitor is never told whether an order exists.', 'sellsmith' ); ?></p>
     770                <?php
     771                self::panel_close();
    747772        }
    748773
  • sellsmith/trunk/admin/class-sellsmith-leads.php

    r3587090 r3712213  
    178178                echo '<form method="get" class="sellsmith-leads-search">';
    179179                echo '<input type="hidden" name="page" value="sellsmith-leads" />';
    180                 echo '<p class="search-box"><input type="search" name="s" value="' . esc_attr( $search ) . '" placeholder="' . esc_attr__( 'Search city or message…', 'sellsmith' ) . '" /> ';
     180                echo '<p class="search-box"><input type="search" name="s" value="' . esc_attr( $search ) . '" placeholder="' . esc_attr__( 'Search email, order, city or message…', 'sellsmith' ) . '" /> ';
    181181                submit_button( __( 'Search', 'sellsmith' ), 'secondary', '', false );
    182182                echo '</p></form>';
    … …  
    285285                $note        = '';
    286286                $contact_val = '';
     287                $meta        = array();
    287288                if ( ! empty( $conv['visitor_meta'] ) ) {
    288                         $meta = json_decode( $conv['visitor_meta'], true );
    289                         if ( is_array( $meta ) ) {
     289                        $decoded = json_decode( $conv['visitor_meta'], true );
     290                        if ( is_array( $decoded ) ) {
     291                                $meta        = $decoded;
    290292                                $note        = isset( $meta['note'] ) ? $meta['note'] : '';
    291293                                $contact_val = isset( $meta['contact'] ) ? $meta['contact'] : '';
    … …  
    297299                self::meta_item( __( 'City', 'sellsmith' ), $conv['visitor_city'] ? $conv['visitor_city'] : '' );
    298300                self::meta_item( __( 'Contact', 'sellsmith' ), $contact_val ? $contact_val : '' );
     301                self::meta_item( __( 'Visitor', 'sellsmith' ), Sellsmith_Conversations::visitor_line( $meta ) );
    299302                self::meta_item( __( 'Note', 'sellsmith' ), $note ? $note : '' );
    300303                self::meta_item( __( 'WhatsApp', 'sellsmith' ), $conv['wa_clicked'] ? __( 'Clicked the button', 'sellsmith' ) : '' );
    301304                echo '</div>';
     305
     306                $order_url = Sellsmith_Conversations::visitor_order_url( $meta );
     307                if ( $order_url ) {
     308                        echo '<p><a class="button" href="' . esc_url( $order_url ) . '">' . esc_html__( 'Open their order', 'sellsmith' ) . '</a></p>';
     309                }
    302310
    303311                $products = self::product_links( $conv['product_ids'] );
    … …  
    361369                }
    362370                $meta = json_decode( $row['visitor_meta'], true );
    363                 if ( ! is_array( $meta ) || empty( $meta['contact'] ) ) {
     371                if ( ! is_array( $meta ) ) {
    364372                        return '';
     373                }
     374
     375                // Who they said they are in the start questions, under the contact.
     376                $who = '';
     377                if ( ! empty( $meta['start'] ) ) {
     378                        if ( 'customer' === $meta['start'] ) {
     379                                $who = __( 'Customer', 'sellsmith' );
     380                                if ( ! empty( $meta['order_number'] ) ) {
     381                                        $who .= ' #' . $meta['order_number'] . ( ( isset( $meta['order_match'] ) && 'found' === $meta['order_match'] ) ? ' ✓' : '' );
     382                                }
     383                        } else {
     384                                $who = __( 'New visitor', 'sellsmith' );
     385                        }
     386                        $who = '<br /><small class="sellsmith-who">' . esc_html( $who ) . '</small>';
     387                }
     388
     389                if ( empty( $meta['contact'] ) ) {
     390                        return $who;
    365391                }
    366392
    … …  
    369395
    370396                if ( 'email' === $type ) {
    371                         return '<a href="' . esc_url( 'mailto:' . $value ) . '">' . esc_html( $value ) . '</a>';
     397                        return '<a href="' . esc_url( 'mailto:' . $value ) . '">' . esc_html( $value ) . '</a>' . $who;
    372398                }
    373399                if ( 'phone' === $type ) {
    374                         return '<a href="' . esc_url( 'https://wa.me/' . rawurlencode( $value ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $value ) . '</a>';
    375                 }
    376                 return esc_html( $value );
     400                        return '<a href="' . esc_url( 'https://wa.me/' . rawurlencode( $value ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $value ) . '</a>' . $who;
     401                }
     402                return esc_html( $value ) . $who;
    377403        }
    378404
  • sellsmith/trunk/assets/admin.css

    r3587090 r3712213  
    872872        color: var(--ss-ink);
    873873}
     874
     875/* customer or new visitor, under the contact in the inbox list */
     876.sellsmith-who { color: var(--ss-muted); }
    874877
    875878/* ---------- leads: transcript ---------- */
  • sellsmith/trunk/assets/widget.css

    r3587090 r3712213  
    246246}
    247247
     248/* start questions ("Are you already a customer?") */
     249#sellsmith-root .ss-start-choices {
     250        display: flex;
     251        flex-wrap: wrap;
     252        justify-content: flex-end;
     253        gap: 7px;
     254        align-self: stretch;
     255        animation: ss-pop .26s cubic-bezier(.22, 1, .36, 1);
     256}
     257#sellsmith-root .ss-start-choice {
     258        min-height: 42px;
     259        padding: 0 15px;
     260        border: 1.5px solid var(--ss-brand);
     261        border-radius: 999px;
     262        background: #fff;
     263        color: var(--ss-brand);
     264        font-size: 14px;
     265        font-weight: 600;
     266        transition: background .15s, color .15s;
     267}
     268#sellsmith-root .ss-start-choice:hover { background: var(--ss-brand); color: var(--ss-on-brand); }
     269#sellsmith-root .ss-start-form {
     270        background: var(--ss-sand);
     271        border-radius: 13px;
     272        border-bottom-left-radius: 5px;
     273        padding: 11px 12px;
     274        align-self: stretch;
     275        display: flex;
     276        flex-direction: column;
     277        gap: 8px;
     278        animation: ss-pop .26s cubic-bezier(.22, 1, .36, 1);
     279}
     280#sellsmith-root .ss-start-form .ss-input { flex: none; width: 100%; min-height: 42px; }
     281#sellsmith-root .ss-start-err { font-size: 13px; color: #b42318; }
     282#sellsmith-root .ss-start-err[hidden] { display: none; }
     283#sellsmith-root .ss-start-go {
     284        min-height: 42px;
     285        border-radius: 10px;
     286        background: var(--ss-brand);
     287        color: var(--ss-on-brand);
     288        font-weight: 600;
     289}
     290#sellsmith-root .ss-start-go:disabled { opacity: .55; cursor: default; }
     291#sellsmith-root .ss-input:disabled { background: #f4f5f7; cursor: not-allowed; }
     292/* 16px stops iPhones from zooming the page when a field is tapped */
     293@media (max-width: 480px) {
     294        #sellsmith-root .ss-start-form .ss-input { font-size: 16px; }
     295}
     296
    248297/* input bar */
    249298#sellsmith-root .ss-bar {
  • sellsmith/trunk/assets/widget.js

    r3587090 r3712213  
    66        var cfg = sellsmithWidget;
    77        var t = cfg.i18n;
     8        // wp_localize_script turns top-level true/1 into "1" and false/0 into "" or "0",
     9        // and "0" is truthy in JS, so on/off settings are read through this.
     10        function on(v) { return v === true || v === 1 || v === "1"; }
    811        var resumeToken = cfg.resumeToken || "";
    912
    … …  
    1114        // so strip it from the address bar right away: that keeps it out of browser
    1215        // history, out of Referer headers, and out of any link the visitor copies.
    13         if (resumeToken && window.history && history.replaceState) {
     16        // Always window.history here: the chat keeps its own "var history" further down,
     17        // which hides the browser's one inside this function and crashed every reply link.
     18        if (resumeToken && window.history && window.history.replaceState) {
    1419                try {
    1520                        var _u = new URL(window.location.href);
    1621                        if (_u.searchParams.has("sellsmith_chat")) {
    1722                                _u.searchParams.delete("sellsmith_chat");
    18                                 history.replaceState(null, "", _u.toString());
     23                                window.history.replaceState(null, "", _u.toString());
    1924                        }
    2025                } catch (e) { /* older browser: leave the URL as-is */ }
    … …  
    3944        var CONSENT_KEY = "sellsmith_consent";
    4045        var EMAIL_ASKED_KEY = "sellsmith_email_asked";
     46        var START_KEY = "sellsmith_start_done";
    4147
    4248        function store(key, val) {
    … …  
    258264        /* ---------- consent gate ---------- */
    259265        function needsConsent() {
    260                 return cfg.consent === 1 && read(CONSENT_KEY) !== "1";
     266                return on(cfg.consent) && read(CONSENT_KEY) !== "1";
    261267        }
    262268
    … …  
    272278                box.appendChild(ok);
    273279                log.appendChild(box);
     280        }
     281
     282        /* ---------- start questions ---------- */
     283        /* "Are you already a customer?" before the first message. Customers give their
     284           order number and email, new visitors their email, so the owner can always
     285           reply, even after the visitor has left. Turned on in Settings > Appearance. */
     286        function historyHasUser() {
     287                for (var i = 0; i < history.length; i++) {
     288                        if (history[i].r === "user") return true;
     289                }
     290                return false;
     291        }
     292
     293        function needsStart() {
     294                return on(cfg.startStep) && !!cfg.startUrl && !resumeToken && read(START_KEY) !== session && !historyHasUser();
     295        }
     296
     297        function lockBar(on) {
     298                input.disabled = on;
     299                sendBtn.disabled = on;
     300                input.placeholder = on ? (t.startLocked || t.placeholder) : t.placeholder;
     301        }
     302
     303        function validEmail(v) {
     304                return /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(v);
     305        }
     306
     307        function showStart() {
     308                lockBar(true);
     309                addBubble("ai", t.startQuestion, true);
     310
     311                var row = el("div", "ss-start-choices");
     312                var yes = el("button", "ss-start-choice", t.startYes);
     313                var no = el("button", "ss-start-choice", t.startNo);
     314                yes.type = "button";
     315                no.type = "button";
     316                row.appendChild(yes);
     317                row.appendChild(no);
     318                log.appendChild(row);
     319                log.scrollTop = log.scrollHeight;
     320
     321                yes.addEventListener("click", function () { row.remove(); startForm("customer"); });
     322                no.addEventListener("click", function () { row.remove(); startForm("new"); });
     323                yes.focus();
     324        }
     325
     326        function startForm(type) {
     327                var customer = type === "customer";
     328                var choice = customer ? t.startYes : t.startNo;
     329                addBubble("user", choice, true);
     330                addBubble("ai", customer ? t.startCustomer : t.startNew, true);
     331
     332                var form = el("form", "ss-start-form");
     333                form.noValidate = true;
     334
     335                var orderInp = null;
     336                if (customer) {
     337                        orderInp = el("input", "ss-input");
     338                        orderInp.type = "text";
     339                        orderInp.maxLength = 40;
     340                        orderInp.autocomplete = "off";
     341                        orderInp.placeholder = t.startOrder;
     342                        orderInp.setAttribute("aria-label", t.startOrder);
     343                        form.appendChild(orderInp);
     344                }
     345
     346                var emailInp = el("input", "ss-input");
     347                emailInp.type = "email";
     348                emailInp.maxLength = 120;
     349                emailInp.autocomplete = "email";
     350                emailInp.placeholder = customer ? t.startEmailBuy : t.startEmailNew;
     351                emailInp.setAttribute("aria-label", emailInp.placeholder);
     352                if (cfg.userEmail) emailInp.value = cfg.userEmail;
     353                form.appendChild(emailInp);
     354
     355                var err = el("p", "ss-start-err");
     356                err.setAttribute("role", "alert");
     357                err.hidden = true;
     358                form.appendChild(err);
     359
     360                var go = el("button", "ss-start-go", t.startGo);
     361                go.type = "submit";
     362                form.appendChild(go);
     363
     364                if (cfg.startEmail === "optional") {
     365                        var skip = el("button", "ss-email-skip", t.startSkip);
     366                        skip.type = "button";
     367                        skip.addEventListener("click", function () {
     368                                submitStart(type, choice, "", orderInp ? orderInp.value.trim() : "", form, go, err);
     369                        });
     370                        form.appendChild(skip);
     371                }
     372
     373                form.addEventListener("submit", function (e) {
     374                        e.preventDefault();
     375                        var email = emailInp.value.trim();
     376                        var bad = email ? !validEmail(email) : cfg.startEmail !== "optional";
     377                        if (bad) {
     378                                err.textContent = t.startBadEmail;
     379                                err.hidden = false;
     380                                emailInp.setAttribute("aria-invalid", "true");
     381                                emailInp.focus();
     382                                return;
     383                        }
     384                        emailInp.removeAttribute("aria-invalid");
     385                        submitStart(type, choice, email, orderInp ? orderInp.value.trim() : "", form, go, err);
     386                });
     387
     388                log.appendChild(form);
     389                log.scrollTop = log.scrollHeight;
     390                (orderInp || (emailInp.value ? go : emailInp)).focus();
     391        }
     392
     393        function submitStart(type, choice, email, order, form, go, err) {
     394                var skipBtn = form.querySelector(".ss-email-skip");
     395                function busyForm(onOff) {
     396                        go.disabled = onOff;
     397                        if (skipBtn) skipBtn.disabled = onOff;
     398                }
     399                busyForm(true);
     400                fetch(cfg.startUrl, {
     401                        method: "POST",
     402                        headers: { "Content-Type": "application/json", "X-WP-Nonce": cfg.nonce },
     403                        body: JSON.stringify({ session_id: session, type: type, email: email, order: order, nonce: cfg.nonce })
     404                })
     405                        .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })
     406                        .then(function (res) {
     407                                if (res.ok && res.d && res.d.ok) {
     408                                        finishStart(type, choice, email, order, form);
     409                                        return;
     410                                }
     411                                busyForm(false);
     412                                err.textContent = (res.d && res.d.message) ? res.d.message : t.error;
     413                                err.hidden = false;
     414                        })
     415                        .catch(function () {
     416                                busyForm(false);
     417                                err.textContent = t.error;
     418                                err.hidden = false;
     419                        });
     420        }
     421
     422        function finishStart(type, choice, email, order, form) {
     423                form.remove();
     424                // Keep the answers in the saved on-screen chat, so a reload reads the same.
     425                history.push({ r: "ai", c: t.startQuestion });
     426                history.push({ r: "user", c: choice });
     427                var parts = [];
     428                if (order) parts.push(t.startOrderShort + " " + order);
     429                if (email) parts.push(email);
     430                if (parts.length) addBubble("user", parts.join(" · "));
     431                addBubble("ai", email ? t.startThanks : t.startThanksShort);
     432                store(START_KEY, session);
     433                // Pro's soft email ask has nothing left to ask for.
     434                if (email) store(EMAIL_ASKED_KEY, session);
     435                lockBar(false);
     436                input.focus();
     437                emit("startdone", { type: type, email: !!email });
    274438        }
    275439
    … …  
    406570                        addBubble("ai", cfg.welcome);
    407571                }
     572                if (needsStart()) { showStart(); return; }
    408573                input.focus();
    409574        }
    … …  
    504669                }
    505670                emit("newchat", { session: session });
     671                if (needsStart()) { showStart(); return; }
     672                lockBar(false);
    506673                input.focus();
    507674        }
  • sellsmith/trunk/includes/class-sellsmith-chat.php

    r3587090 r3712213  
    253253                $user_count  = Sellsmith_Conversations::count_user_messages( $conversation_id );
    254254
    255                 if ( $has_contact && ! $had_contact ) {
    256                         // They just left their contact: confirm once, then go quiet.
     255                if ( ( $has_contact && ! $had_contact ) || ( $had_contact && 1 === $user_count && '' !== $message ) ) {
     256                        // They just left their contact, or gave it in the start questions and this
     257                        // is their first message: confirm once, then go quiet.
    257258                        $reply = __( 'Got it! 🙏 We will reply to you here soon. You can add more details if you like.', 'sellsmith' );
    258259                } elseif ( $has_contact ) {
  • sellsmith/trunk/includes/class-sellsmith-conversations.php

    r3587090 r3712213  
    305305                $meta['contact']      = $value;
    306306                $meta['contact_type'] = $type;
    307                 $meta['mode']         = 'message';
     307                // Only tag a conversation that has no mode yet. A live chat must stay live,
     308                // or it drops out of the owner's live inbox the moment the visitor saves an email.
     309                if ( empty( $meta['mode'] ) ) {
     310                        $meta['mode'] = 'message';
     311                }
    308312
    309313                $wpdb->update(
    … …  
    318322
    319323                return $type;
     324        }
     325
     326        /**
     327         * Save the answers from the start questions ("Are you already a customer?").
     328         * Never changes the conversation mode, and never replaces a contact that is
     329         * already saved on it.
     330         *
     331         * @param int   $conversation_id Conversation id.
     332         * @param array $data            start ('customer'|'new'), email, order_number,
     333         *                               plus the order check fields for customers.
     334         */
     335        public static function save_start( $conversation_id, $data ) {
     336                $conversation_id = (int) $conversation_id;
     337                $meta            = self::get_meta( $conversation_id );
     338
     339                $changes = array(
     340                        'start'    => ( isset( $data['start'] ) && 'customer' === $data['start'] ) ? 'customer' : 'new',
     341                        'start_at' => time(),
     342                );
     343
     344                $email = isset( $data['email'] ) ? sanitize_email( (string) $data['email'] ) : '';
     345                if ( '' !== $email && is_email( $email ) && empty( $meta['contact'] ) ) {
     346                        $changes['contact']      = $email;
     347                        $changes['contact_type'] = 'email';
     348                }
     349
     350                foreach ( array( 'order_number', 'order_match' ) as $key ) {
     351                        $changes[ $key ] = isset( $data[ $key ] ) ? (string) $data[ $key ] : '';
     352                }
     353                foreach ( array( 'order_id', 'orders_by_email', 'last_order_id' ) as $key ) {
     354                        $changes[ $key ] = isset( $data[ $key ] ) ? absint( $data[ $key ] ) : 0;
     355                }
     356
     357                self::merge_meta( $conversation_id, $changes );
     358
     359                do_action( 'sellsmith_lead_updated', $conversation_id );
     360        }
     361
     362        /**
     363         * One plain line for the owner saying who the visitor is, from the start
     364         * questions. Admin, owner alerts and the live console only: it names orders,
     365         * so it is never sent to the visitor.
     366         *
     367         * @param array $meta    Decoded visitor_meta.
     368         * @param bool  $details Add the order's products, date and status (loads the order).
     369         * @return string Empty when the visitor never answered the start questions.
     370         */
     371        public static function visitor_line( $meta, $details = true ) {
     372                if ( ! is_array( $meta ) || empty( $meta['start'] ) ) {
     373                        return '';
     374                }
     375                if ( 'customer' !== $meta['start'] ) {
     376                        return __( 'New visitor', 'sellsmith' );
     377                }
     378
     379                $number = isset( $meta['order_number'] ) ? (string) $meta['order_number'] : '';
     380                $match  = isset( $meta['order_match'] ) ? (string) $meta['order_match'] : '';
     381                $id     = isset( $meta['order_id'] ) ? (int) $meta['order_id'] : 0;
     382
     383                if ( '' === $number ) {
     384                        $count = isset( $meta['orders_by_email'] ) ? (int) $meta['orders_by_email'] : 0;
     385                        $last  = isset( $meta['last_order_id'] ) ? (int) $meta['last_order_id'] : 0;
     386                        if ( ! $count ) {
     387                                return __( 'Customer, no order number given', 'sellsmith' ) . ( function_exists( 'wc_get_order' ) ? ', ' . __( 'no orders under this email', 'sellsmith' ) : '' );
     388                        }
     389                        /* translators: 1: number of orders ("5+" at most), 2: newest order number. */
     390                        return sprintf( __( 'Customer, no order number given, %1$s orders under this email (newest #%2$d)', 'sellsmith' ), $count >= 5 ? '5+' : (string) $count, $last );
     391                }
     392
     393                switch ( $match ) {
     394                        case 'found':
     395                                $line = sprintf(
     396                                        /* translators: %s: order number. */
     397                                        __( 'Customer, order #%s checked', 'sellsmith' ),
     398                                        $number
     399                                );
     400                                $more = $details ? self::order_details( $id ) : '';
     401                                return '' !== $more ? $line . ': ' . $more : $line;
     402                        case 'email_mismatch':
     403                                /* translators: %s: order number. */
     404                                return sprintf( __( 'Customer, order #%s exists but under a different email', 'sellsmith' ), $number );
     405                        case 'not_found':
     406                                /* translators: %s: order number. */
     407                                return sprintf( __( 'Customer, order #%s not found in this shop', 'sellsmith' ), $number );
     408                        default:
     409                                /* translators: %s: order number. */
     410                                return sprintf( __( 'Customer, order #%s', 'sellsmith' ), $number );
     411                }
     412        }
     413
     414        /**
     415         * Products, date and status of an order, for the owner.
     416         *
     417         * @param int $order_id Order id.
     418         * @return string
     419         */
     420        private static function order_details( $order_id ) {
     421                if ( ! $order_id || ! function_exists( 'wc_get_order' ) ) {
     422                        return '';
     423                }
     424                $order = wc_get_order( $order_id );
     425                if ( ! $order || ! is_a( $order, 'WC_Order' ) ) {
     426                        return '';
     427                }
     428
     429                $names = array();
     430                foreach ( $order->get_items() as $item ) {
     431                        $names[] = $item->get_name();
     432                }
     433
     434                $parts = array();
     435                if ( $names ) {
     436                        $parts[] = implode( ', ', array_slice( $names, 0, 3 ) ) . ( count( $names ) > 3 ? ' +' . ( count( $names ) - 3 ) : '' );
     437                }
     438                $created = $order->get_date_created();
     439                if ( $created ) {
     440                        $parts[] = $created->date_i18n( get_option( 'date_format' ) );
     441                }
     442                $parts[] = wc_get_order_status_name( $order->get_status() );
     443
     444                return implode( ', ', $parts );
     445        }
     446
     447        /**
     448         * Admin link to the order a customer named, when it exists in this shop.
     449         *
     450         * @param array $meta Decoded visitor_meta.
     451         * @return string URL or ''.
     452         */
     453        public static function visitor_order_url( $meta ) {
     454                $id = ( is_array( $meta ) && ! empty( $meta['order_id'] ) ) ? (int) $meta['order_id'] : 0;
     455                if ( ! $id && is_array( $meta ) && empty( $meta['order_number'] ) && ! empty( $meta['last_order_id'] ) ) {
     456                        $id = (int) $meta['last_order_id'];
     457                }
     458                if ( ! $id || ! function_exists( 'wc_get_order' ) || ! current_user_can( 'edit_shop_orders' ) ) {
     459                        return '';
     460                }
     461                $order = wc_get_order( $id );
     462                return ( $order && is_a( $order, 'WC_Order' ) ) ? $order->get_edit_order_url() : '';
    320463        }
    321464
    … …  
    621764                if ( ! empty( $args['search'] ) ) {
    622765                        $like   = '%' . $wpdb->esc_like( $args['search'] ) . '%';
    623                         $where .= " AND (c.visitor_city LIKE %s OR c.session_id LIKE %s OR EXISTS (SELECT 1 FROM {$msg} m2 WHERE m2.conversation_id = c.id AND m2.content LIKE %s))";
    624                         $params = array( $like, $like, $like );
     766                        // visitor_meta holds the saved email and any order number from the start questions.
     767                        $where .= " AND (c.visitor_city LIKE %s OR c.session_id LIKE %s OR c.visitor_meta LIKE %s OR EXISTS (SELECT 1 FROM {$msg} m2 WHERE m2.conversation_id = c.id AND m2.content LIKE %s))";
     768                        $params = array( $like, $like, $like, $like );
    625769                }
    626770
    … …  
    712856
    713857        /**
    714          * Conversation ids whose messages contain a given email (privacy tools).
     858         * Conversation ids that hold a given email, in a message or as the saved
     859         * contact (privacy tools).
    715860         *
    716861         * @param string $email Email address.
    … …  
    719864        public static function ids_by_email( $email, $page = 1 ) {
    720865                global $wpdb;
    721                 $msg = $wpdb->prefix . 'sw_messages';
     866                $conv = $wpdb->prefix . 'sw_conversations';
     867                $msg  = $wpdb->prefix . 'sw_messages';
    722868
    723869                $like   = '%' . $wpdb->esc_like( $email ) . '%';
    … …  
    726872                $ids = $wpdb->get_col(
    727873                        $wpdb->prepare(
    728                                 "SELECT DISTINCT conversation_id FROM {$msg} WHERE content LIKE %s ORDER BY conversation_id ASC LIMIT 10 OFFSET %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
     874                                "SELECT c.id FROM {$conv} c WHERE c.visitor_meta LIKE %s OR EXISTS (SELECT 1 FROM {$msg} m WHERE m.conversation_id = c.id AND m.content LIKE %s) ORDER BY c.id ASC LIMIT 10 OFFSET %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
     875                                $like,
    729876                                $like,
    730877                                $offset
  • sellsmith/trunk/includes/class-sellsmith-privacy.php

    r3587090 r3712213  
    6969                        }
    7070
     71                        $meta  = Sellsmith_Conversations::meta( $id );
     72                        $items = array(
     73                                array(
     74                                        'name'  => __( 'Started', 'sellsmith' ),
     75                                        'value' => $conv['started_at'],
     76                                ),
     77                                array(
     78                                        'name'  => __( 'City', 'sellsmith' ),
     79                                        'value' => $conv['visitor_city'],
     80                                ),
     81                        );
     82                        if ( ! empty( $meta['contact'] ) ) {
     83                                $items[] = array(
     84                                        'name'  => __( 'Contact', 'sellsmith' ),
     85                                        'value' => $meta['contact'],
     86                                );
     87                        }
     88                        if ( ! empty( $meta['order_number'] ) ) {
     89                                $items[] = array(
     90                                        'name'  => __( 'Order number', 'sellsmith' ),
     91                                        'value' => $meta['order_number'],
     92                                );
     93                        }
     94                        $items[] = array(
     95                                'name'  => __( 'Transcript', 'sellsmith' ),
     96                                'value' => $transcript,
     97                        );
     98
    7199                        $data[] = array(
    72100                                'group_id'    => 'sellsmith_conversations',
    73101                                'group_label' => __( 'Chat conversations', 'sellsmith' ),
    74102                                'item_id'     => 'sellsmith-conversation-' . $id,
    75                                 'data'        => array(
    76                                         array(
    77                                                 'name'  => __( 'Started', 'sellsmith' ),
    78                                                 'value' => $conv['started_at'],
    79                                         ),
    80                                         array(
    81                                                 'name'  => __( 'City', 'sellsmith' ),
    82                                                 'value' => $conv['visitor_city'],
    83                                         ),
    84                                         array(
    85                                                 'name'  => __( 'Transcript', 'sellsmith' ),
    86                                                 'value' => $transcript,
    87                                         ),
    88                                 ),
     103                                'data'        => $items,
    89104                        );
    90105                }
    … …  
    103118         */
    104119        public static function erase( $email, $page = 1 ) {
    105                 $ids = Sellsmith_Conversations::ids_by_email( $email, $page );
     120                // Always page 1: each pass deletes what it found, so an offset would skip rows.
     121                $ids = Sellsmith_Conversations::ids_by_email( $email, 1 );
    106122
    107123                if ( $ids ) {
  • sellsmith/trunk/includes/class-sellsmith-rest.php

    r3587090 r3712213  
    9393                                        ),
    9494                                        'nonce' => array(
     95                                                'type'              => 'string',
     96                                                'required'          => false,
     97                                                'sanitize_callback' => 'sanitize_text_field',
     98                                        ),
     99                                ),
     100                        )
     101                );
     102
     103                register_rest_route(
     104                        self::NS,
     105                        '/start',
     106                        array(
     107                                'methods'             => WP_REST_Server::CREATABLE,
     108                                'callback'            => array( __CLASS__, 'start' ),
     109                                'permission_callback' => array( __CLASS__, 'verify_widget_nonce' ),
     110                                'args'                => array(
     111                                        'session_id' => array(
     112                                                'type'              => 'string',
     113                                                'required'          => true,
     114                                                'sanitize_callback' => 'sanitize_text_field',
     115                                        ),
     116                                        'type'       => array(
     117                                                'type'              => 'string',
     118                                                'required'          => true,
     119                                                'sanitize_callback' => 'sanitize_key',
     120                                        ),
     121                                        'email'      => array(
     122                                                'type'              => 'string',
     123                                                'required'          => false,
     124                                                'sanitize_callback' => 'sanitize_text_field',
     125                                        ),
     126                                        'order'      => array(
     127                                                'type'              => 'string',
     128                                                'required'          => false,
     129                                                'sanitize_callback' => 'sanitize_text_field',
     130                                        ),
     131                                        'nonce'      => array(
    95132                                                'type'              => 'string',
    96133                                                'required'          => false,
    … …  
    191228
    192229                return rest_ensure_response( $payload );
     230        }
     231
     232        /**
     233         * Save the answers to the start questions: customer or not, their email, and
     234         * for customers the order number, checked against this shop's orders. The
     235         * answer never says whether an order exists; only the owner sees that.
     236         *
     237         * @param WP_REST_Request $request Request.
     238         */
     239        public static function start( WP_REST_Request $request ) {
     240
     241                if ( ! Sellsmith_Settings::get( 'widget_enabled' ) || ! Sellsmith_Settings::get( 'start_step' ) ) {
     242                        return new WP_Error( 'sellsmith_off', __( 'Chat is not available right now.', 'sellsmith' ), array( 'status' => 403 ) );
     243                }
     244
     245                // Per-IP rate limit, so the form cannot be used to flood the inbox with rows.
     246                $ip   = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
     247                $key  = 'sellsmith_start_' . md5( $ip );
     248                $hits = (int) get_transient( $key );
     249                if ( $hits >= 30 ) {
     250                        return new WP_Error( 'sellsmith_rate', __( 'Too many requests. Please wait a moment.', 'sellsmith' ), array( 'status' => 429 ) );
     251                }
     252                set_transient( $key, $hits + 1, HOUR_IN_SECONDS );
     253
     254                $session_id = (string) $request->get_param( 'session_id' );
     255                if ( ! preg_match( '/^[A-Za-z0-9\-]{8,64}$/', $session_id ) ) {
     256                        return new WP_Error( 'sellsmith_session', __( 'Please refresh the page and try again.', 'sellsmith' ), array( 'status' => 400 ) );
     257                }
     258
     259                $type = (string) $request->get_param( 'type' );
     260                if ( ! in_array( $type, array( 'customer', 'new' ), true ) ) {
     261                        return new WP_Error( 'sellsmith_type', __( 'Please pick an answer.', 'sellsmith' ), array( 'status' => 400 ) );
     262                }
     263
     264                $email = trim( (string) $request->get_param( 'email' ) );
     265                if ( '' !== $email ) {
     266                        $email = sanitize_email( $email );
     267                        if ( ! $email || ! is_email( $email ) ) {
     268                                return new WP_Error( 'sellsmith_email', __( 'Please enter a valid email.', 'sellsmith' ), array( 'status' => 400 ) );
     269                        }
     270                } elseif ( 'optional' !== Sellsmith_Settings::get( 'start_email' ) ) {
     271                        return new WP_Error( 'sellsmith_email', __( 'Please enter your email.', 'sellsmith' ), array( 'status' => 400 ) );
     272                }
     273
     274                $order = '';
     275                if ( 'customer' === $type ) {
     276                        $order = preg_replace( '/[^A-Za-z0-9\-_\/]/', '', ltrim( trim( (string) $request->get_param( 'order' ) ), '#' ) );
     277                        $order = substr( (string) $order, 0, 40 );
     278                }
     279
     280                $conversation_id = Sellsmith_Conversations::get_or_create( $session_id );
     281                if ( ! $conversation_id ) {
     282                        return new WP_Error( 'sellsmith_conv', __( 'Something went wrong. Please try again.', 'sellsmith' ), array( 'status' => 500 ) );
     283                }
     284
     285                $data = array(
     286                        'start'        => $type,
     287                        'email'        => $email,
     288                        'order_number' => $order,
     289                );
     290                if ( 'customer' === $type ) {
     291                        $data = array_merge( $data, self::match_order( $order, $email ) );
     292                }
     293
     294                Sellsmith_Conversations::save_start( $conversation_id, $data );
     295
     296                return rest_ensure_response( array( 'ok' => true ) );
     297        }
     298
     299        /**
     300         * Look a customer's order up in WooCommerce. An order only counts as found
     301         * when its billing email is the email they typed.
     302         *
     303         * @param string $number Order number as typed (cleaned), or ''.
     304         * @param string $email  Email as typed (valid), or ''.
     305         * @return array order_match ('found'|'email_mismatch'|'not_found'|''), order_id,
     306         *               orders_by_email, last_order_id.
     307         */
     308        private static function match_order( $number, $email ) {
     309                $out = array(
     310                        'order_match'     => '',
     311                        'order_id'        => 0,
     312                        'orders_by_email' => 0,
     313                        'last_order_id'   => 0,
     314                );
     315
     316                if ( ! sellsmith_has_wc() || ! function_exists( 'wc_get_order' ) ) {
     317                        return $out;
     318                }
     319
     320                if ( '' !== $email && function_exists( 'wc_get_orders' ) ) {
     321                        $ids = wc_get_orders(
     322                                array(
     323                                        'billing_email' => $email,
     324                                        'limit'         => 5,
     325                                        'orderby'       => 'date',
     326                                        'order'         => 'DESC',
     327                                        'return'        => 'ids',
     328                                )
     329                        );
     330                        $ids                    = is_array( $ids ) ? array_values( $ids ) : array();
     331                        $out['orders_by_email'] = count( $ids );
     332                        $out['last_order_id']   = $ids ? (int) $ids[0] : 0;
     333                }
     334
     335                if ( '' === $number ) {
     336                        return $out;
     337                }
     338
     339                // Same filter as WooCommerce's own order tracking form, so shops that use
     340                // custom order numbers (sequential number plugins) map them to the real id.
     341                $id    = (string) apply_filters( 'woocommerce_shortcode_order_tracking_order_id', $number );
     342                $order = ctype_digit( $id ) ? wc_get_order( (int) $id ) : false;
     343
     344                if ( ! $order || ! is_a( $order, 'WC_Order' ) ) {
     345                        $out['order_match'] = 'not_found';
     346                        return $out;
     347                }
     348
     349                $out['order_id']    = $order->get_id();
     350                $out['order_match'] = ( '' !== $email && strtolower( $order->get_billing_email() ) === strtolower( $email ) ) ? 'found' : 'email_mismatch';
     351
     352                return $out;
    193353        }
    194354
  • sellsmith/trunk/includes/class-sellsmith-settings.php

    r3587090 r3712213  
    4848                        'teaser_text'     => __( 'Need help finding something?', 'sellsmith' ),
    4949                        'show_credit'     => 0,
     50                        // Start questions (asked when the chat opens).
     51                        'start_step'      => 0,
     52                        'start_email'     => 'required',
     53                        'start_question'  => __( 'Are you already a customer?', 'sellsmith' ),
     54                        'start_yes'       => __( 'Yes, I have an order', 'sellsmith' ),
     55                        'start_no'        => __( 'No, not yet', 'sellsmith' ),
    5056                        // WhatsApp.
    5157                        'wa_number'       => '',
    … …  
    6975                        'connection' => array( 'api_key', 'model', 'custom_model' ),
    7076                        'profile'    => array( 'store_about', 'delivery_info', 'delivery_areas', 'cod_enabled', 'open_parcel', 'return_policy', 'discount_info', 'payment_methods', 'extra_info' ),
    71                         'appearance' => array( 'widget_position', 'widget_color', 'widget_title', 'welcome_message', 'teaser_text', 'show_credit' ),
     77                        'appearance' => array( 'widget_position', 'widget_color', 'widget_title', 'welcome_message', 'teaser_text', 'show_credit', 'start_step', 'start_email', 'start_question', 'start_yes', 'start_no' ),
    7278                        'whatsapp'   => array( 'wa_number', 'wa_enabled' ),
    7379                        'advanced'   => array( 'rate_limit', 'max_messages', 'retention_days', 'consent_required', 'keep_data' ),
    … …  
    7985         */
    8086        public static function checkbox_keys() {
    81                 return array( 'cod_enabled', 'widget_enabled', 'wa_enabled', 'consent_required', 'keep_data', 'show_credit' );
     87                return array( 'cod_enabled', 'widget_enabled', 'wa_enabled', 'consent_required', 'keep_data', 'show_credit', 'start_step' );
    8288        }
    8389
    … …  
    169175                                case 'chat_mode':
    170176                                        $existing[ $key ] = in_array( $raw, array( 'ai', 'message' ), true ) ? $raw : 'ai';
     177                                        break;
     178
     179                                case 'start_email':
     180                                        $existing[ $key ] = in_array( $raw, array( 'required', 'optional' ), true ) ? $raw : 'required';
     181                                        break;
     182
     183                                case 'start_question':
     184                                case 'start_yes':
     185                                case 'start_no':
     186                                        // A cleared field falls back to the default wording, never an empty button.
     187                                        $raw              = sanitize_text_field( $raw );
     188                                        $existing[ $key ] = '' !== $raw ? $raw : self::defaults()[ $key ];
    171189                                        break;
    172190
  • sellsmith/trunk/includes/class-sellsmith-widget.php

    r3587090 r3712213  
    7373                        'context'  => self::page_context(),
    7474                        'showCredit' => ! empty( $s['show_credit'] ),
     75                        // Start questions ("Are you already a customer?"), asked when the chat opens.
     76                        'startStep'  => ! empty( $s['start_step'] ) ? 1 : 0,
     77                        'startEmail' => ( 'optional' === $s['start_email'] ) ? 'optional' : 'required',
     78                        'startUrl'   => esc_url_raw( rest_url( 'sellsmith/v1/start' ) ),
     79                        // A logged-in visitor's own email, to fill the field in for them.
     80                        'userEmail'  => ( ! empty( $s['start_step'] ) && is_user_logged_in() ) ? wp_get_current_user()->user_email : '',
    7581                        'i18n'     => array(
    7682                                'placeholder' => __( 'Type a message', 'sellsmith' ),
    … …  
    9096                                'sent'         => __( 'Sent', 'sellsmith' ),
    9197                                'seen'         => __( 'Seen', 'sellsmith' ),
     98                                // Start questions.
     99                                'startQuestion'   => '' !== trim( (string) $s['start_question'] ) ? $s['start_question'] : Sellsmith_Settings::defaults()['start_question'],
     100                                'startYes'        => '' !== trim( (string) $s['start_yes'] ) ? $s['start_yes'] : Sellsmith_Settings::defaults()['start_yes'],
     101                                'startNo'         => '' !== trim( (string) $s['start_no'] ) ? $s['start_no'] : Sellsmith_Settings::defaults()['start_no'],
     102                                'startCustomer'   => __( 'Great! What is your order number, and the email you ordered with?', 'sellsmith' ),
     103                                'startNew'        => __( 'What is your email? If we cannot reply right away, we will answer you there.', 'sellsmith' ),
     104                                'startOrder'      => __( 'Order number (if you have it)', 'sellsmith' ),
     105                                'startEmailBuy'   => __( 'Email you ordered with', 'sellsmith' ),
     106                                'startEmailNew'   => __( 'Your email', 'sellsmith' ),
     107                                'startGo'         => __( 'Continue', 'sellsmith' ),
     108                                'startSkip'       => __( 'Skip', 'sellsmith' ),
     109                                'startBadEmail'   => __( 'Please enter a valid email.', 'sellsmith' ),
     110                                'startOrderShort' => __( 'Order', 'sellsmith' ),
     111                                'startThanks'     => ( 'message' === $s['chat_mode'] )
     112                                        ? __( 'Thanks! Now type your question below. If we are not here right away, we will reply to you by email.', 'sellsmith' )
     113                                        : __( 'Thanks! Now type your question below.', 'sellsmith' ),
     114                                'startThanksShort' => __( 'Thanks! Now type your question below.', 'sellsmith' ),
     115                                'startLocked'     => __( 'Pick an answer above first', 'sellsmith' ),
    92116                        ),
    93117                );
  • sellsmith/trunk/languages/sellsmith.pot

    r3587090 r3712213  
     1# Copyright (C) 2026 DevTonic Studios
     2# This file is distributed under the GPLv2 or later.
    13msgid ""
    24msgstr ""
    3 "Project-Id-Version: Sellsmith 1.1.0\n"
     5"Project-Id-Version: Sellsmith – AI Chatbot, Lead Capture & Sales Chat 1.2.0\n"
     6"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/sellsmith\n"
     7"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
     8"Language-Team: LANGUAGE <LL@li.org>\n"
    49"MIME-Version: 1.0\n"
    510"Content-Type: text/plain; charset=UTF-8\n"
    611"Content-Transfer-Encoding: 8bit\n"
    7 "POT-Creation-Date: 2026-06-12 23:16+0000\n"
     12"POT-Creation-Date: 2026-09-25T00:52:32+00:00\n"
     13"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
     14"X-Generator: WP-CLI 2.12.0\n"
    815"X-Domain: sellsmith\n"
    916
     17#. Plugin Name of the plugin
     18#: sellsmith.php
     19msgid "Sellsmith – AI Chatbot, Lead Capture & Sales Chat"
     20msgstr ""
     21
     22#. Plugin URI of the plugin
     23#: sellsmith.php
     24msgid "https://devtonicstudios.com/sellsmith"
     25msgstr ""
     26
     27#. Description of the plugin
     28#: sellsmith.php
     29msgid "An AI sales agent for your store. Answers buyers from your real product data, captures leads, and hands hot buyers to your WhatsApp. Runs on your own free Gemini API key."
     30msgstr ""
     31
     32#. Author of the plugin
     33#: sellsmith.php
     34msgid "DevTonic Studios"
     35msgstr ""
     36
     37#. Author URI of the plugin
     38#: sellsmith.php
     39msgid "https://devtonicstudios.com"
     40msgstr ""
     41
     42#: admin/class-sellsmith-admin.php:33
     43#: admin/class-sellsmith-admin.php:34
     44msgid "Sellsmith"
     45msgstr ""
     46
     47#: admin/class-sellsmith-admin.php:71
     48msgid "WooCommerce is not active. Sellsmith will still chat and capture leads, and product answers will switch on automatically when WooCommerce is installed."
     49msgstr ""
     50
     51#: admin/class-sellsmith-admin.php:100
     52msgid "Testing…"
     53msgstr ""
     54
     55#: admin/class-sellsmith-admin.php:118
     56msgid "AI key"
     57msgstr ""
     58
     59#: admin/class-sellsmith-admin.php:119
     60msgid "Business profile"
     61msgstr ""
     62
     63#: admin/class-sellsmith-admin.php:120
     64msgid "Appearance"
     65msgstr ""
     66
     67#: admin/class-sellsmith-admin.php:121
     68#: admin/class-sellsmith-leads.php:197
     69#: admin/class-sellsmith-leads.php:303
     70msgid "WhatsApp"
     71msgstr ""
     72
     73#: admin/class-sellsmith-admin.php:122
     74msgid "Advanced"
     75msgstr ""
     76
     77#: admin/class-sellsmith-admin.php:123
     78#: admin/class-sellsmith-admin.php:872
     79msgid "Sellsmith Pro"
     80msgstr ""
     81
     82#: admin/class-sellsmith-admin.php:219
     83#: admin/class-sellsmith-admin.php:318
     84msgid "Chat on my store"
     85msgstr ""
     86
     87#: admin/class-sellsmith-admin.php:221
     88msgid "Buyers can see and use the chat."
     89msgstr ""
     90
     91#: admin/class-sellsmith-admin.php:221
     92msgid "Paused. Buyers do not see the chat right now."
     93msgstr ""
     94
     95#: admin/class-sellsmith-admin.php:228
     96msgid "How it answers buyers"
     97msgstr ""
     98
     99#: admin/class-sellsmith-admin.php:234
     100msgid "AI answers"
     101msgstr ""
     102
     103#: admin/class-sellsmith-admin.php:236
     104msgid "The AI replies about products, price and delivery."
     105msgstr ""
     106
     107#: admin/class-sellsmith-admin.php:237
     108msgid "Needs your Gemini key. Add it in the AI key settings."
     109msgstr ""
     110
     111#: admin/class-sellsmith-admin.php:242
     112msgid "Just collect messages"
     113msgstr ""
     114
     115#: admin/class-sellsmith-admin.php:243
     116msgid "No AI. Save each question and contact as a lead."
     117msgstr ""
     118
     119#: admin/class-sellsmith-admin.php:250
     120msgid "Also let buyers reach a person"
     121msgstr ""
     122
     123#: admin/class-sellsmith-admin.php:266
     124msgid "Open settings"
     125msgstr ""
     126
     127#: admin/class-sellsmith-admin.php:267
     128msgid "Open inbox"
     129msgstr ""
     130
     131#: admin/class-sellsmith-admin.php:284
     132msgid "Right now your chat is OFF. Buyers do not see a chat on your store. Turn it on below."
     133msgstr ""
     134
     135#: admin/class-sellsmith-admin.php:288
     136msgid "Your chat is ON. It saves each buyer's question and contact as a lead."
     137msgstr ""
     138
     139#: admin/class-sellsmith-admin.php:292
     140msgid "Your chat is ON. The AI answers buyers about products, price and delivery."
     141msgstr ""
     142
     143#: admin/class-sellsmith-admin.php:296
     144msgid "Your chat is ON, but the AI needs your Gemini key to answer. Add it in the AI key settings."
     145msgstr ""
     146
     147#: admin/class-sellsmith-admin.php:357
     148#: admin/class-sellsmith-admin.php:852
     149msgid "Live chat"
     150msgstr ""
     151
     152#: admin/class-sellsmith-admin.php:357
     153msgid "Pro"
     154msgstr ""
     155
     156#: admin/class-sellsmith-admin.php:358
     157msgid "Reply to buyers yourself in real time, with no page refresh. In Sellsmith Pro."
     158msgstr ""
     159
     160#: admin/class-sellsmith-admin.php:379
     161msgid "Home"
     162msgstr ""
     163
     164#: admin/class-sellsmith-admin.php:415
     165msgid "Save settings"
     166msgstr ""
     167
     168#: admin/class-sellsmith-admin.php:446
     169msgid "Setup complete. Your chat is live."
     170msgstr ""
     171
     172#: admin/class-sellsmith-admin.php:452
     173msgid "1. Connect your free Gemini key"
     174msgstr ""
     175
     176#: admin/class-sellsmith-admin.php:453
     177msgid "2. Tell the AI about your store"
     178msgstr ""
     179
     180#: admin/class-sellsmith-admin.php:454
     181msgid "3. Turn the chat on"
     182msgstr ""
     183
     184#. translators: %d: completed steps out of 3
     185#: admin/class-sellsmith-admin.php:461
     186#, php-format
     187msgid "Setup: %d of 3 steps done"
     188msgstr ""
     189
     190#. translators: %s: link to Google AI Studio
     191#: admin/class-sellsmith-admin.php:548
     192#, php-format
     193msgid "Get a free Gemini API key from %s (takes 1 minute, no card needed)."
     194msgstr ""
     195
     196#: admin/class-sellsmith-admin.php:553
     197msgid "Connect to Gemini"
     198msgstr ""
     199
     200#: admin/class-sellsmith-admin.php:557
     201msgid "Gemini API key"
     202msgstr ""
     203
     204#: admin/class-sellsmith-admin.php:559
     205msgid "A key is saved. Leave blank to keep it, or paste a new key to replace it."
     206msgstr ""
     207
     208#: admin/class-sellsmith-admin.php:560
     209msgid "Paste your key, then press Test connection."
     210msgstr ""
     211
     212#: admin/class-sellsmith-admin.php:566
     213msgid "AI model"
     214msgstr ""
     215
     216#: admin/class-sellsmith-admin.php:574
     217msgid "Gemma gives the most free chats per day; the Gemini models give more polished replies. All five support product lookups."
     218msgstr ""
     219
     220#: admin/class-sellsmith-admin.php:577
     221msgid "Free-tier limits by model"
     222msgstr ""
     223
     224#: admin/class-sellsmith-admin.php:581
     225msgid "Model"
     226msgstr ""
     227
     228#: admin/class-sellsmith-admin.php:582
     229msgid "Free chats / day*"
     230msgstr ""
     231
     232#: admin/class-sellsmith-admin.php:583
     233msgid "Per minute"
     234msgstr ""
     235
     236#: admin/class-sellsmith-admin.php:584
     237msgid "Product lookups"
     238msgstr ""
     239
     240#: admin/class-sellsmith-admin.php:588
     241#: admin/class-sellsmith-admin.php:589
     242#: admin/class-sellsmith-admin.php:590
     243#: admin/class-sellsmith-admin.php:591
     244#: admin/class-sellsmith-admin.php:592
     245msgid "Yes"
     246msgstr ""
     247
     248#. translators: %s: link to Google AI Studio
     249#: admin/class-sellsmith-admin.php:599
     250#, php-format
     251msgid "*Free limits depend on your Google account and region. These are typical values. Gemma gives the most free chats but slightly rougher replies. Paid use is cheapest on the Flash-Lite models. %s."
     252msgstr ""
     253
     254#: admin/class-sellsmith-admin.php:600
     255msgid "Check your live limits"
     256msgstr ""
     257
     258#: admin/class-sellsmith-admin.php:608
     259msgid "Custom model id (optional)"
     260msgstr ""
     261
     262#: admin/class-sellsmith-admin.php:609
     263msgid "For advanced users. If filled, this overrides the dropdown. Useful when Google releases new models."
     264msgstr ""
     265
     266#: admin/class-sellsmith-admin.php:613
     267msgid "Test your key"
     268msgstr ""
     269
     270#: admin/class-sellsmith-admin.php:616
     271msgid "Test connection"
     272msgstr ""
     273
     274#: admin/class-sellsmith-admin.php:625
     275msgid "Important: keep the free tier free."
     276msgstr ""
     277
     278#: admin/class-sellsmith-admin.php:626
     279msgid "Do not enable billing on the Google Cloud project of this key. If billing is on, Google charges every request instead of using the free daily limit. A key from a fresh AI Studio project is perfect."
     280msgstr ""
     281
     282#: admin/class-sellsmith-admin.php:635
     283msgid "The AI answers from this page plus your live product data. These are the questions real buyers ask on WhatsApp. Write your answers in any language, the AI adapts."
     284msgstr ""
     285
     286#: admin/class-sellsmith-admin.php:638
     287msgid "About your store"
     288msgstr ""
     289
     290#: admin/class-sellsmith-admin.php:642
     291msgid "Store overview"
     292msgstr ""
     293
     294#: admin/class-sellsmith-admin.php:644
     295msgid "Example: Stride sells men and women sports shoes. Based in Lahore, shipping all Pakistan since 2021."
     296msgstr ""
     297
     298#: admin/class-sellsmith-admin.php:649
     299msgid "Delivery"
     300msgstr ""
     301
     302#: admin/class-sellsmith-admin.php:653
     303msgid "Delivery charges and time"
     304msgstr ""
     305
     306#: admin/class-sellsmith-admin.php:654
     307msgid "Buyers ask: \"What are delivery charges?\" and \"When will I receive it?\""
     308msgstr ""
     309
     310#: admin/class-sellsmith-admin.php:655
     311msgid "Example: Rs 200 flat, free over Rs 5,000. Lahore 1-2 days, rest of Pakistan 2-4 days."
     312msgstr ""
     313
     314#: admin/class-sellsmith-admin.php:659
     315msgid "Delivery areas"
     316msgstr ""
     317
     318#: admin/class-sellsmith-admin.php:660
     319msgid "Buyers ask: \"Do you deliver to my area?\""
     320msgstr ""
     321
     322#: admin/class-sellsmith-admin.php:661
     323msgid "Example: All Pakistan via Leopards and TCS. No international shipping."
     324msgstr ""
     325
     326#: admin/class-sellsmith-admin.php:665
     327msgid "Cash on delivery"
     328msgstr ""
     329
     330#: admin/class-sellsmith-admin.php:666
     331msgid "We accept cash on delivery"
     332msgstr ""
     333
     334#: admin/class-sellsmith-admin.php:671
     335msgid "Policies & payment"
     336msgstr ""
     337
     338#: admin/class-sellsmith-admin.php:675
     339msgid "Open parcel policy"
     340msgstr ""
     341
     342#: admin/class-sellsmith-admin.php:676
     343msgid "Buyers ask: \"Can I open and check the parcel before paying?\" Very common in COD markets."
     344msgstr ""
     345
     346#: admin/class-sellsmith-admin.php:677
     347msgid "Example: Yes, you can open and check the parcel in front of the rider before paying."
     348msgstr ""
     349
     350#: admin/class-sellsmith-admin.php:681
     351msgid "Faults, returns and warranty"
     352msgstr ""
     353
     354#: admin/class-sellsmith-admin.php:682
     355msgid "Buyers ask: \"What if the product has a fault?\""
     356msgstr ""
     357
     358#: admin/class-sellsmith-admin.php:683
     359msgid "Example: 7-day free replacement for any fault. Exchange only, no cash refund."
     360msgstr ""
     361
     362#: admin/class-sellsmith-admin.php:687
     363msgid "Discounts and bulk deals"
     364msgstr ""
     365
     366#: admin/class-sellsmith-admin.php:688
     367msgid "Buyers ask: \"Any discount?\" and \"What if I buy 2?\""
     368msgstr ""
     369
     370#: admin/class-sellsmith-admin.php:689
     371msgid "Example: 10% off on 2 or more pairs. No other discounts right now."
     372msgstr ""
     373
     374#: admin/class-sellsmith-admin.php:693
     375msgid "Payment methods"
     376msgstr ""
     377
     378#: admin/class-sellsmith-admin.php:695
     379msgid "Example: COD, JazzCash, Easypaisa, bank transfer."
     380msgstr ""
     381
     382#: admin/class-sellsmith-admin.php:700
     383msgid "Anything else"
     384msgstr ""
     385
     386#: admin/class-sellsmith-admin.php:704
     387msgid "Notes for the AI"
     388msgstr ""
     389
     390#: admin/class-sellsmith-admin.php:705
     391msgid "Size guides, store timings, special instructions. Optional."
     392msgstr ""
     393
     394#: admin/class-sellsmith-admin.php:713
     395msgid "Look and feel"
     396msgstr ""
     397
     398#: admin/class-sellsmith-admin.php:716
     399msgid "Position"
     400msgstr ""
     401
     402#: admin/class-sellsmith-admin.php:718
     403msgid "Bottom right"
     404msgstr ""
     405
     406#: admin/class-sellsmith-admin.php:719
     407msgid "Bottom left (good for RTL sites)"
     408msgstr ""
     409
     410#: admin/class-sellsmith-admin.php:723
     411msgid "Brand color"
     412msgstr ""
     413
     414#: admin/class-sellsmith-admin.php:726
     415msgid "Pick a color"
     416msgstr ""
     417
     418#: admin/class-sellsmith-admin.php:733
     419msgid "This one color is used across the whole chat: the button, the header, and your reply bubbles. Click the swatch to pick, or type a hex code like #a0561b."
     420msgstr ""
     421
     422#: admin/class-sellsmith-admin.php:736
     423msgid "Chat title"
     424msgstr ""
     425
     426#: admin/class-sellsmith-admin.php:737
     427msgid "Welcome message"
     428msgstr ""
     429
     430#: admin/class-sellsmith-admin.php:738
     431msgid "Pop-up greeting"
     432msgstr ""
     433
     434#: admin/class-sellsmith-admin.php:741
     435msgid "Credit link"
     436msgstr ""
     437
     438#: admin/class-sellsmith-admin.php:742
     439msgid "Show a small \"AI agent by Sellsmith\" link at the bottom of the chat"
     440msgstr ""
     441
     442#: admin/class-sellsmith-admin.php:748
     443msgid "When the chat opens"
     444msgstr ""
     445
     446#: admin/class-sellsmith-admin.php:753
     447msgid "Start questions"
     448msgstr ""
     449
     450#: admin/class-sellsmith-admin.php:754
     451msgid "Ask \"Are you already a customer?\" first. Customers give their order number and email, new visitors give their email. Then they type their question."
     452msgstr ""
     453
     454#: admin/class-sellsmith-admin.php:758
     455msgid "Email"
     456msgstr ""
     457
     458#: admin/class-sellsmith-admin.php:760
     459msgid "Required, so you can always reply"
     460msgstr ""
     461
     462#: admin/class-sellsmith-admin.php:761
     463msgid "Optional, visitors can skip it"
     464msgstr ""
     465
     466#: admin/class-sellsmith-admin.php:765
     467msgid "Question"
     468msgstr ""
     469
     470#: admin/class-sellsmith-admin.php:766
     471msgid "Button for customers"
     472msgstr ""
     473
     474#: admin/class-sellsmith-admin.php:767
     475msgid "Button for new visitors"
     476msgstr ""
     477
     478#: admin/class-sellsmith-admin.php:769
     479msgid "With WooCommerce, the order number is checked against your orders. Only you see the result, in the Inbox. The visitor is never told whether an order exists."
     480msgstr ""
     481
     482#: admin/class-sellsmith-admin.php:775
     483#: admin/class-sellsmith-admin.php:787
     484msgid "WhatsApp handoff"
     485msgstr ""
     486
     487#: admin/class-sellsmith-admin.php:780
     488msgid "WhatsApp number"
     489msgstr ""
     490
     491#: admin/class-sellsmith-admin.php:781
     492msgid "International format, digits only, no + and no spaces. Example for Pakistan: 923001234567"
     493msgstr ""
     494
     495#: admin/class-sellsmith-admin.php:788
     496msgid "Let the AI offer a WhatsApp button to serious buyers, with their order details pre-written"
     497msgstr ""
     498
     499#: admin/class-sellsmith-admin.php:795
     500msgid "No WhatsApp Business API and no Meta approval needed. The button uses an official wa.me link that opens your normal WhatsApp."
     501msgstr ""
     502
     503#: admin/class-sellsmith-admin.php:801
     504msgid "Limits & privacy"
     505msgstr ""
     506
     507#: admin/class-sellsmith-admin.php:806
     508msgid "Message limit per chat, then WhatsApp"
     509msgstr ""
     510
     511#: admin/class-sellsmith-admin.php:807
     512msgid "After this many buyer messages in one chat, the assistant stops and offers the WhatsApp button instead. Keeps each chat cheap and sends hot buyers straight to you. Default 10."
     513msgstr ""
     514
     515#: admin/class-sellsmith-admin.php:812
     516msgid "Message limit per visitor per hour"
     517msgstr ""
     518
     519#: admin/class-sellsmith-admin.php:813
     520msgid "Protects your free daily AI limit from abuse. Default 20."
     521msgstr ""
     522
     523#: admin/class-sellsmith-admin.php:818
     524msgid "Auto-delete old conversations after (days)"
     525msgstr ""
     526
     527#: admin/class-sellsmith-admin.php:819
     528msgid "Leave 0 to keep every conversation forever. Set a number (for example 90) and conversations older than that are removed automatically each day, to save space and respect privacy."
     529msgstr ""
     530
     531#: admin/class-sellsmith-admin.php:824
     532msgid "Privacy consent"
     533msgstr ""
     534
     535#: admin/class-sellsmith-admin.php:825
     536msgid "Ask visitors to accept a short privacy note before the first message (recommended for EU stores)"
     537msgstr ""
     538
     539#: admin/class-sellsmith-admin.php:829
     540msgid "On uninstall"
     541msgstr ""
     542
     543#: admin/class-sellsmith-admin.php:830
     544msgid "Keep my settings and saved conversations if I delete the plugin"
     545msgstr ""
     546
     547#: admin/class-sellsmith-admin.php:837
     548msgid "Privacy note: visitor chat messages are sent to Google Gemini for processing using your API key. Mention this in your privacy policy."
     549msgstr ""
     550
     551#: admin/class-sellsmith-admin.php:852
     552msgid "Reply to buyers yourself in real time."
     553msgstr ""
     554
     555#: admin/class-sellsmith-admin.php:853
     556msgid "Lead scoring"
     557msgstr ""
     558
     559#: admin/class-sellsmith-admin.php:853
     560msgid "See Hot, Warm, and Cold buyers at a glance."
     561msgstr ""
     562
     563#: admin/class-sellsmith-admin.php:854
     564msgid "Hot-lead alerts"
     565msgstr ""
     566
     567#: admin/class-sellsmith-admin.php:854
     568msgid "Get an email the moment a serious buyer chats."
     569msgstr ""
     570
     571#: admin/class-sellsmith-admin.php:855
     572msgid "Reply by email"
     573msgstr ""
     574
     575#: admin/class-sellsmith-admin.php:855
     576msgid "Answer a saved chat straight from your inbox."
     577msgstr ""
     578
     579#: admin/class-sellsmith-admin.php:856
     580msgid "Agent branding"
     581msgstr ""
     582
     583#: admin/class-sellsmith-admin.php:856
     584msgid "Show your photo or logo as the chat avatar."
     585msgstr ""
     586
     587#: admin/class-sellsmith-admin.php:862
     588msgid "Sellsmith Pro is active. Here is everything it adds and where to set each one up."
     589msgstr ""
     590
     591#: admin/class-sellsmith-admin.php:864
     592msgid "Sellsmith Pro is installed. Activate your license to switch these on."
     593msgstr ""
     594
     595#: admin/class-sellsmith-admin.php:866
     596msgid "These features are part of Sellsmith Pro."
     597msgstr ""
     598
     599#: admin/class-sellsmith-admin.php:873
     600msgid "Active"
     601msgstr ""
     602
     603#: admin/class-sellsmith-admin.php:884
     604#: admin/class-sellsmith-leads.php:44
     605#: admin/class-sellsmith-leads.php:45
     606#: sellsmith.php:103
    10607msgid "Settings"
    11608msgstr ""
    12609
    13 msgid "Sellsmith"
    14 msgstr ""
    15 
    16 msgid "WooCommerce is not active. Sellsmith will still chat and capture leads, and product answers will switch on automatically when WooCommerce is installed."
    17 msgstr ""
    18 
    19 msgid "Testing…"
    20 msgstr ""
    21 
    22 msgid "Connection"
    23 msgstr ""
    24 
    25 msgid "Business Profile"
    26 msgstr ""
    27 
    28 msgid "Chat Widget"
    29 msgstr ""
    30 
    31 msgid "WhatsApp"
    32 msgstr ""
    33 
    34 msgid "Advanced"
    35 msgstr ""
    36 
    37 msgid "Save settings"
    38 msgstr ""
    39 
    40 msgid "1. Connect your free Gemini key"
    41 msgstr ""
    42 
    43 msgid "2. Fill your business profile"
    44 msgstr ""
    45 
    46 msgid "3. Add your WhatsApp number"
    47 msgstr ""
    48 
    49 msgid "Setup complete. Turn on the widget in the Chat Widget tab and your assistant is live."
    50 msgstr ""
    51 
    52 msgid "Setup: %d of 3 steps done"
    53 msgstr ""
    54 
    55 msgid "Get a free Gemini API key from %s (takes 1 minute, no card needed)."
    56 msgstr ""
    57 
    58 msgid "Gemini API key"
    59 msgstr ""
    60 
    61 msgid "A key is saved. Leave blank to keep it, or paste a new key to replace it."
    62 msgstr ""
    63 
    64 msgid "Paste your key, then press Test connection."
    65 msgstr ""
    66 
    67 msgid "AI model"
    68 msgstr ""
    69 
    70 msgid "Flash Lite gives the biggest free daily limit and is fast enough for sales chat."
    71 msgstr ""
    72 
    73 msgid "Custom model id (optional)"
    74 msgstr ""
    75 
    76 msgid "For advanced users. If filled, this overrides the dropdown. Useful when Google releases new models."
    77 msgstr ""
    78 
    79 msgid "Test"
    80 msgstr ""
    81 
    82 msgid "Test connection"
    83 msgstr ""
    84 
    85 msgid "Important: keep the free tier free."
    86 msgstr ""
    87 
    88 msgid "Do not enable billing on the Google Cloud project of this key. If billing is on, Google charges every request instead of using the free daily limit. A key from a fresh AI Studio project is perfect."
    89 msgstr ""
    90 
    91 msgid "The AI answers from this page plus your live product data. These are the questions real buyers ask on WhatsApp. Write your answers in any language, the AI adapts."
    92 msgstr ""
    93 
    94 msgid "About your store"
    95 msgstr ""
    96 
    97 msgid "Example: Stride sells men and women sports shoes. Based in Lahore, shipping all Pakistan since 2021."
    98 msgstr ""
    99 
    100 msgid "Delivery charges and time"
    101 msgstr ""
    102 
    103 msgid "Buyers ask: \"What are delivery charges?\" and \"When will I receive it?\""
    104 msgstr ""
    105 
    106 msgid "Example: Rs 200 flat, free over Rs 5,000. Lahore 1-2 days, rest of Pakistan 2-4 days."
    107 msgstr ""
    108 
    109 msgid "Delivery areas"
    110 msgstr ""
    111 
    112 msgid "Buyers ask: \"Do you deliver to my area?\""
    113 msgstr ""
    114 
    115 msgid "Example: All Pakistan via Leopards and TCS. No international shipping."
    116 msgstr ""
    117 
    118 msgid "Cash on delivery"
    119 msgstr ""
    120 
    121 msgid "We accept cash on delivery"
    122 msgstr ""
    123 
    124 msgid "Open parcel policy"
    125 msgstr ""
    126 
    127 msgid "Buyers ask: \"Can I open and check the parcel before paying?\" Very common in COD markets."
    128 msgstr ""
    129 
    130 msgid "Example: Yes, you can open and check the parcel in front of the rider before paying."
    131 msgstr ""
    132 
    133 msgid "Faults, returns and warranty"
    134 msgstr ""
    135 
    136 msgid "Buyers ask: \"What if the product has a fault?\""
    137 msgstr ""
    138 
    139 msgid "Example: 7-day free replacement for any fault. Exchange only, no cash refund."
    140 msgstr ""
    141 
    142 msgid "Discounts and bulk deals"
    143 msgstr ""
    144 
    145 msgid "Buyers ask: \"Any discount?\" and \"What if I buy 2?\""
    146 msgstr ""
    147 
    148 msgid "Example: 10% off on 2 or more pairs. No other discounts right now."
    149 msgstr ""
    150 
    151 msgid "Payment methods"
    152 msgstr ""
    153 
    154 msgid "Example: COD, JazzCash, Easypaisa, bank transfer."
    155 msgstr ""
    156 
    157 msgid "Anything else the AI should know"
    158 msgstr ""
    159 
    160 msgid "Size guides, store timings, special instructions. Optional."
    161 msgstr ""
    162 
    163 msgid "Show widget"
    164 msgstr ""
    165 
    166 msgid "Show the chat widget on my store"
    167 msgstr ""
    168 
    169 msgid "Position"
    170 msgstr ""
    171 
    172 msgid "Bottom right"
    173 msgstr ""
    174 
    175 msgid "Bottom left (good for RTL sites)"
    176 msgstr ""
    177 
    178 msgid "Brand color"
    179 msgstr ""
    180 
    181 msgid "Widget title"
    182 msgstr ""
    183 
    184 msgid "Welcome message"
    185 msgstr ""
    186 
    187 msgid "Teaser bubble text"
    188 msgstr ""
    189 
    190 msgid "WhatsApp number"
    191 msgstr ""
    192 
    193 msgid "International format, digits only, no + and no spaces. Example for Pakistan: 923001234567"
    194 msgstr ""
    195 
    196 msgid "WhatsApp handoff"
    197 msgstr ""
    198 
    199 msgid "Let the AI offer a WhatsApp button to serious buyers, with their order details pre-written"
    200 msgstr ""
    201 
    202 msgid "No WhatsApp Business API and no Meta approval needed. The button uses an official wa.me link that opens your normal WhatsApp."
    203 msgstr ""
    204 
    205 msgid "Messages per visitor per hour"
    206 msgstr ""
    207 
    208 msgid "Protects your free daily AI limit from abuse. Default 20."
    209 msgstr ""
    210 
    211 msgid "Privacy consent"
    212 msgstr ""
    213 
    214 msgid "Ask visitors to accept a short privacy note before the first message (recommended for EU stores)"
    215 msgstr ""
    216 
    217 msgid "On uninstall"
    218 msgstr ""
    219 
    220 msgid "Keep my settings and saved conversations if I delete the plugin"
    221 msgstr ""
    222 
    223 msgid "Privacy note: visitor chat messages are sent to Google Gemini for processing using your API key. Mention this in your privacy policy."
    224 msgstr ""
    225 
    226 msgid "Leads"
    227 msgstr ""
    228 
     610#: admin/class-sellsmith-admin.php:892
     611msgid "Activate your license"
     612msgstr ""
     613
     614#: admin/class-sellsmith-admin.php:898
     615msgid "Get Sellsmith Pro"
     616msgstr ""
     617
     618#: admin/class-sellsmith-leads.php:26
     619#: admin/class-sellsmith-leads.php:27
     620msgid "Dashboard"
     621msgstr ""
     622
     623#: admin/class-sellsmith-leads.php:34
     624#: admin/class-sellsmith-leads.php:35
     625#: admin/class-sellsmith-leads.php:107
     626msgid "Inbox"
     627msgstr ""
     628
     629#: admin/class-sellsmith-leads.php:101
     630msgid "All chats"
     631msgstr ""
     632
     633#: admin/class-sellsmith-leads.php:156
    229634msgid "Deleted."
    230635msgstr ""
    231636
     637#: admin/class-sellsmith-leads.php:161
    232638msgid "Chats today"
    233639msgstr ""
    234640
     641#: admin/class-sellsmith-leads.php:162
    235642msgid "Chats, last 7 days"
    236643msgstr ""
    237644
     645#: admin/class-sellsmith-leads.php:163
    238646msgid "WhatsApp clicks, last 7 days"
    239647msgstr ""
    240648
    241 msgid "Free version keeps the last 30 days of conversations."
    242 msgstr ""
    243 
    244 msgid "Search city or message…"
    245 msgstr ""
    246 
     649#. translators: %d: number of days.
     650#: admin/class-sellsmith-leads.php:170
     651#, php-format
     652msgid "Conversations older than %d days are deleted automatically. Change this in Settings → Advanced."
     653msgstr ""
     654
     655#: admin/class-sellsmith-leads.php:174
     656msgid "All conversations are kept. Set an auto-delete period in Settings → Advanced if you prefer."
     657msgstr ""
     658
     659#: admin/class-sellsmith-leads.php:180
     660msgid "Search email, order, city or message…"
     661msgstr ""
     662
     663#: admin/class-sellsmith-leads.php:181
    247664msgid "Search"
    248665msgstr ""
    249666
     667#: admin/class-sellsmith-leads.php:191
     668#: admin/class-sellsmith-leads.php:298
     669#: includes/class-sellsmith-privacy.php:74
    250670msgid "Started"
    251671msgstr ""
    252672
     673#: admin/class-sellsmith-leads.php:192
    253674msgid "First message"
    254675msgstr ""
    255676
     677#: admin/class-sellsmith-leads.php:193
     678#: admin/class-sellsmith-leads.php:299
     679#: includes/class-sellsmith-privacy.php:78
    256680msgid "City"
    257681msgstr ""
    258682
     683#: admin/class-sellsmith-leads.php:194
     684#: admin/class-sellsmith-leads.php:300
     685#: includes/class-sellsmith-privacy.php:84
     686msgid "Contact"
     687msgstr ""
     688
     689#: admin/class-sellsmith-leads.php:195
    259690msgid "Products"
    260691msgstr ""
    261692
     693#: admin/class-sellsmith-leads.php:196
    262694msgid "Messages"
    263695msgstr ""
    264696
     697#: admin/class-sellsmith-leads.php:204
    265698msgid "No conversations yet. They will appear here as soon as visitors start chatting."
    266699msgstr ""
    267700
     701#: admin/class-sellsmith-leads.php:214
    268702msgid "(no message)"
    269703msgstr ""
    270704
     705#: admin/class-sellsmith-leads.php:223
    271706msgid "View"
    272707msgstr ""
    273708
     709#: admin/class-sellsmith-leads.php:224
     710#: admin/class-sellsmith-leads.php:347
    274711msgid "Delete this conversation?"
    275712msgstr ""
    276713
     714#: admin/class-sellsmith-leads.php:224
    277715msgid "Delete"
    278716msgstr ""
    279717
     718#: admin/class-sellsmith-leads.php:229
    280719msgid "clicked"
    281720msgstr ""
    282721
     722#: admin/class-sellsmith-leads.php:243
    283723msgid "Delete selected conversations?"
    284724msgstr ""
    285725
     726#: admin/class-sellsmith-leads.php:243
    286727msgid "Delete selected"
    287728msgstr ""
    288729
    289 msgid "Back to leads"
    290 msgstr ""
    291 
     730#: admin/class-sellsmith-leads.php:272
     731msgid "Back to inbox"
     732msgstr ""
     733
     734#: admin/class-sellsmith-leads.php:275
    292735msgid "Conversation not found"
    293736msgstr ""
    294737
     738#: admin/class-sellsmith-leads.php:282
    295739msgid "Conversation"
    296740msgstr ""
    297741
     742#: admin/class-sellsmith-leads.php:301
     743msgid "Visitor"
     744msgstr ""
     745
     746#: admin/class-sellsmith-leads.php:302
    298747msgid "Note"
    299748msgstr ""
    300749
     750#: admin/class-sellsmith-leads.php:303
    301751msgid "Clicked the button"
    302752msgstr ""
    303753
     754#: admin/class-sellsmith-leads.php:308
     755msgid "Open their order"
     756msgstr ""
     757
     758#: admin/class-sellsmith-leads.php:313
    304759msgid "Products discussed:"
    305760msgstr ""
    306761
     762#: admin/class-sellsmith-leads.php:322
    307763msgid "Buyer"
    308764msgstr ""
    309765
     766#: admin/class-sellsmith-leads.php:324
     767msgid "You"
     768msgstr ""
     769
     770#: admin/class-sellsmith-leads.php:326
    310771msgid "AI"
    311772msgstr ""
    312773
     774#: admin/class-sellsmith-leads.php:347
    313775msgid "Delete conversation"
    314776msgstr ""
    315777
     778#: admin/class-sellsmith-leads.php:379
     779msgid "Customer"
     780msgstr ""
     781
     782#: admin/class-sellsmith-leads.php:384
     783#: includes/class-sellsmith-conversations.php:376
     784msgid "New visitor"
     785msgstr ""
     786
     787#: includes/class-sellsmith-chat.php:28
     788#: includes/class-sellsmith-chat.php:90
     789#: includes/class-sellsmith-rest.php:242
     790msgid "Chat is not available right now."
     791msgstr ""
     792
     793#: includes/class-sellsmith-chat.php:36
     794#: includes/class-sellsmith-rest.php:256
     795msgid "Please refresh the page and try again."
     796msgstr ""
     797
     798#: includes/class-sellsmith-chat.php:49
     799msgid "You are sending messages very fast. Please wait a minute and try again."
     800msgstr ""
     801
     802#: includes/class-sellsmith-chat.php:94
     803msgid "Please type a message first."
     804msgstr ""
     805
     806#: includes/class-sellsmith-chat.php:99
     807#: includes/class-sellsmith-chat.php:154
     808msgid "Our assistant is taking a short break. Message us directly and we will reply fast."
     809msgstr ""
     810
     811#: includes/class-sellsmith-chat.php:105
     812#: includes/class-sellsmith-chat.php:222
     813#: includes/class-sellsmith-rest.php:282
     814msgid "Something went wrong. Please try again."
     815msgstr ""
     816
     817#: includes/class-sellsmith-chat.php:112
     818msgid "Thanks for chatting! To get full details and place your order, please message us on WhatsApp. Our team will help you finish up there."
     819msgstr ""
     820
     821#: includes/class-sellsmith-chat.php:217
     822msgid "This chat link is no longer valid. Please start a new chat."
     823msgstr ""
     824
     825#: includes/class-sellsmith-chat.php:234
     826msgid "Thanks! We have your messages and will reply to you soon."
     827msgstr ""
     828
     829#: includes/class-sellsmith-chat.php:258
     830msgid "Got it! 🙏 We will reply to you here soon. You can add more details if you like."
     831msgstr ""
     832
     833#: includes/class-sellsmith-chat.php:264
     834msgid "Thanks for your message! What is the best email or WhatsApp number to reach you?"
     835msgstr ""
     836
     837#: includes/class-sellsmith-chat.php:267
     838msgid "Please also leave your email or WhatsApp number so we can reply."
     839msgstr ""
     840
     841#: includes/class-sellsmith-conversations.php:387
     842msgid "Customer, no order number given"
     843msgstr ""
     844
     845#: includes/class-sellsmith-conversations.php:387
     846msgid "no orders under this email"
     847msgstr ""
     848
     849#. translators: 1: number of orders ("5+" at most), 2: newest order number.
     850#: includes/class-sellsmith-conversations.php:390
     851#, php-format
     852msgid "Customer, no order number given, %1$s orders under this email (newest #%2$d)"
     853msgstr ""
     854
     855#. translators: %s: order number.
     856#: includes/class-sellsmith-conversations.php:397
     857#, php-format
     858msgid "Customer, order #%s checked"
     859msgstr ""
     860
     861#. translators: %s: order number.
     862#: includes/class-sellsmith-conversations.php:404
     863#, php-format
     864msgid "Customer, order #%s exists but under a different email"
     865msgstr ""
     866
     867#. translators: %s: order number.
     868#: includes/class-sellsmith-conversations.php:407
     869#, php-format
     870msgid "Customer, order #%s not found in this shop"
     871msgstr ""
     872
     873#. translators: %s: order number.
     874#: includes/class-sellsmith-conversations.php:410
     875#, php-format
     876msgid "Customer, order #%s"
     877msgstr ""
     878
     879#: includes/class-sellsmith-gemini.php:27
     880msgid "Please enter an API key first."
     881msgstr ""
     882
     883#. translators: %s: error message
     884#: includes/class-sellsmith-gemini.php:45
     885#, php-format
     886msgid "Could not reach Google: %s"
     887msgstr ""
     888
     889#: includes/class-sellsmith-gemini.php:53
     890msgid "Unknown error."
     891msgstr ""
     892
     893#. translators: 1: HTTP status code, 2: API error message
     894#: includes/class-sellsmith-gemini.php:57
     895#, php-format
     896msgid "Key rejected (HTTP %1$d): %2$s"
     897msgstr ""
     898
     899#. translators: %d: number of models available
     900#: includes/class-sellsmith-gemini.php:76
     901#, php-format
     902msgid "Key is valid. Connection working. %d models available to this key."
     903msgstr ""
     904
     905#. translators: %s: model id
     906#: includes/class-sellsmith-gemini.php:83
     907#, php-format
     908msgid "Note: the selected model \"%s\" was not in the list. It may still work, or pick another model."
     909msgstr ""
     910
     911#: includes/class-sellsmith-install.php:19
    316912msgid "Sellsmith requires PHP 7.4 or newer. Please ask your host to upgrade PHP."
    317913msgstr ""
    318914
     915#: includes/class-sellsmith-install.php:20
    319916msgid "Plugin activation error"
    320917msgstr ""
    321918
     919#: includes/class-sellsmith-privacy.php:31
     920#: includes/class-sellsmith-privacy.php:44
     921msgid "Sellsmith chat conversations"
     922msgstr ""
     923
     924#: includes/class-sellsmith-privacy.php:90
     925msgid "Order number"
     926msgstr ""
     927
     928#: includes/class-sellsmith-privacy.php:95
     929msgid "Transcript"
     930msgstr ""
     931
     932#: includes/class-sellsmith-privacy.php:101
     933msgid "Chat conversations"
     934msgstr ""
     935
     936#: includes/class-sellsmith-privacy.php:146
     937msgid "This site uses an AI chat assistant (Sellsmith). Messages you type in the chat are sent to Google Gemini for processing in order to generate replies. Conversations, including any city or contact details you share, are stored on this site so the store can follow up on your questions, and may be deleted after a retention period the store has set."
     938msgstr ""
     939
     940#: includes/class-sellsmith-rest.php:184
     941msgid "Your session expired. Please refresh the page and try again."
     942msgstr ""
     943
     944#: includes/class-sellsmith-rest.php:201
     945#: includes/class-sellsmith-rest.php:250
     946#: includes/class-sellsmith-rest.php:368
     947msgid "Too many requests. Please wait a moment."
     948msgstr ""
     949
     950#: includes/class-sellsmith-rest.php:261
     951msgid "Please pick an answer."
     952msgstr ""
     953
     954#: includes/class-sellsmith-rest.php:268
     955#: includes/class-sellsmith-widget.php:109
     956msgid "Please enter a valid email."
     957msgstr ""
     958
     959#: includes/class-sellsmith-rest.php:271
     960msgid "Please enter your email."
     961msgstr ""
     962
     963#: includes/class-sellsmith-rest.php:374
     964msgid "This chat is no longer available."
     965msgstr ""
     966
     967#: includes/class-sellsmith-settings.php:46
     968msgid "Store Assistant"
     969msgstr ""
     970
     971#: includes/class-sellsmith-settings.php:47
     972msgid "Salam! Ask me about any product, price, or delivery."
     973msgstr ""
     974
     975#: includes/class-sellsmith-settings.php:48
     976msgid "Need help finding something?"
     977msgstr ""
     978
     979#: includes/class-sellsmith-settings.php:53
     980msgid "Are you already a customer?"
     981msgstr ""
     982
     983#: includes/class-sellsmith-settings.php:54
     984msgid "Yes, I have an order"
     985msgstr ""
     986
     987#: includes/class-sellsmith-settings.php:55
     988msgid "No, not yet"
     989msgstr ""
     990
     991#: includes/class-sellsmith-settings.php:95
     992msgid "Gemma 4 31B: most free chats/day (~1,500), basic replies"
     993msgstr ""
     994
     995#: includes/class-sellsmith-settings.php:96
     996msgid "Gemini 3.1 Flash-Lite: recommended (~500/day, smart + free)"
     997msgstr ""
     998
     999#: includes/class-sellsmith-settings.php:97
     1000msgid "Gemini 3.5 Flash: smartest replies, low free limit"
     1001msgstr ""
     1002
     1003#: includes/class-sellsmith-settings.php:98
     1004msgid "Gemini 2.5 Flash-Lite: cheapest for paid use"
     1005msgstr ""
     1006
     1007#: includes/class-sellsmith-settings.php:99
     1008msgid "Gemini 2.5 Flash: stable all-rounder"
     1009msgstr ""
     1010
     1011#: includes/class-sellsmith-widget.php:82
    3221012msgid "Type a message"
    3231013msgstr ""
    3241014
     1015#: includes/class-sellsmith-widget.php:83
    3251016msgid "Send message"
    3261017msgstr ""
    3271018
     1019#: includes/class-sellsmith-widget.php:84
    3281020msgid "Open chat"
    3291021msgstr ""
    3301022
     1023#: includes/class-sellsmith-widget.php:85
    3311024msgid "Close chat"
    3321025msgstr ""
    3331026
     1027#: includes/class-sellsmith-widget.php:86
     1028msgid "Start new chat"
     1029msgstr ""
     1030
     1031#: includes/class-sellsmith-widget.php:87
    3341032msgid "Online · replies in seconds"
    3351033msgstr ""
    3361034
     1035#: includes/class-sellsmith-widget.php:88
    3371036msgid "Chat on WhatsApp"
    3381037msgstr ""
    3391038
     1039#: includes/class-sellsmith-widget.php:89
    3401040msgid "Continue on WhatsApp"
    3411041msgstr ""
    3421042
     1043#: includes/class-sellsmith-widget.php:90
    3431044msgid "This chat uses AI (Google Gemini) to answer. Your messages are processed by Google."
    3441045msgstr ""
    3451046
     1047#: includes/class-sellsmith-widget.php:91
    3461048msgid "Okay, start chat"
    3471049msgstr ""
    3481050
     1051#: includes/class-sellsmith-widget.php:92
    3491052msgid "Could not send. Please try again."
    3501053msgstr ""
    3511054
     1055#: includes/class-sellsmith-widget.php:93
    3521056msgid "AI agent by"
    3531057msgstr ""
    3541058
    355 msgid "Sellsmith chat conversations"
    356 msgstr ""
    357 
    358 msgid "Chat conversations"
    359 msgstr ""
    360 
    361 msgid "Transcript"
    362 msgstr ""
    363 
    364 msgid "This site uses an AI chat assistant (Sellsmith). Messages you type in the chat are sent to Google Gemini for processing in order to generate replies. Conversations, including any city or contact details you share, are stored on this site for up to 30 days so the store can follow up on your questions."
    365 msgstr ""
    366 
    367 msgid "Store Assistant"
    368 msgstr ""
    369 
    370 msgid "Salam! Ask me about any product, price, or delivery."
    371 msgstr ""
    372 
    373 msgid "Need help finding something?"
    374 msgstr ""
    375 
    376 msgid "Gemini Flash Lite (recommended, highest free limit)"
    377 msgstr ""
    378 
    379 msgid "Gemini Flash (smarter, lower free limit)"
    380 msgstr ""
    381 
    382 msgid "Chat is not available right now."
    383 msgstr ""
    384 
    385 msgid "Please refresh the page and try again."
    386 msgstr ""
    387 
    388 msgid "Please type a message first."
    389 msgstr ""
    390 
    391 msgid "You are sending messages very fast. Please wait a minute and try again."
    392 msgstr ""
    393 
    394 msgid "Our assistant is taking a short break. Message us directly and we will reply fast."
    395 msgstr ""
    396 
    397 msgid "Something went wrong. Please try again."
    398 msgstr ""
    399 
    400 msgid "Please enter an API key first."
    401 msgstr ""
    402 
    403 msgid "Could not reach Google: %s"
    404 msgstr ""
    405 
    406 msgid "Unknown error."
    407 msgstr ""
    408 
    409 msgid "Key rejected (HTTP %1$d): %2$s"
    410 msgstr ""
    411 
    412 msgid "Key is valid. Connection working. %d models available to this key."
    413 msgstr ""
    414 
    415 msgid "Note: the selected model \"%s\" was not in the list. It may still work, or pick another model."
    416 msgstr ""
     1059#: includes/class-sellsmith-widget.php:94
     1060msgid "Your email or WhatsApp number"
     1061msgstr ""
     1062
     1063#: includes/class-sellsmith-widget.php:95
     1064msgid "Hi! 👋 Ask your question here and we will get back to you."
     1065msgstr ""
     1066
     1067#: includes/class-sellsmith-widget.php:96
     1068msgid "Sent"
     1069msgstr ""
     1070
     1071#: includes/class-sellsmith-widget.php:97
     1072msgid "Seen"
     1073msgstr ""
     1074
     1075#: includes/class-sellsmith-widget.php:102
     1076msgid "Great! What is your order number, and the email you ordered with?"
     1077msgstr ""
     1078
     1079#: includes/class-sellsmith-widget.php:103
     1080msgid "What is your email? If we cannot reply right away, we will answer you there."
     1081msgstr ""
     1082
     1083#: includes/class-sellsmith-widget.php:104
     1084msgid "Order number (if you have it)"
     1085msgstr ""
     1086
     1087#: includes/class-sellsmith-widget.php:105
     1088msgid "Email you ordered with"
     1089msgstr ""
     1090
     1091#: includes/class-sellsmith-widget.php:106
     1092msgid "Your email"
     1093msgstr ""
     1094
     1095#: includes/class-sellsmith-widget.php:107
     1096msgid "Continue"
     1097msgstr ""
     1098
     1099#: includes/class-sellsmith-widget.php:108
     1100msgid "Skip"
     1101msgstr ""
     1102
     1103#: includes/class-sellsmith-widget.php:110
     1104msgid "Order"
     1105msgstr ""
     1106
     1107#: includes/class-sellsmith-widget.php:112
     1108msgid "Thanks! Now type your question below. If we are not here right away, we will reply to you by email."
     1109msgstr ""
     1110
     1111#: includes/class-sellsmith-widget.php:113
     1112#: includes/class-sellsmith-widget.php:114
     1113msgid "Thanks! Now type your question below."
     1114msgstr ""
     1115
     1116#: includes/class-sellsmith-widget.php:115
     1117msgid "Pick an answer above first"
     1118msgstr ""
  • sellsmith/trunk/readme.txt

    r3630208 r3712213  
    55Tested up to: 7.0
    66Requires PHP: 7.4
    7 Stable tag: 1.1.0
     7Stable tag: 1.2.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    5050* Chat widget with your color, position, and welcome message
    5151* Two modes: AI mode (answers from your products) or Message mode (no API key, just collect each visitor's question and contact as a lead)
     52* Start questions: the chat can open with "Are you already a customer?". Customers give their order number and email, new visitors give their email, so you can always reply, even after they leave
    5253* 5-minute setup wizard with a 3-step checklist
    5354* Business profile built from the 9 real buyer questions, including cash on delivery and open parcel policy
    … …  
    138139== Changelog ==
    139140
     141= 1.2.0 =
     142* New: Start questions. The chat can open with "Are you already a customer?". Customers give their order number and the email they ordered with, new visitors give their email, before they type. Turn it on in Settings, Appearance. The email can be required or optional.
     143* New: With WooCommerce, the order number is checked against your orders. The result shows only in your Inbox, never to the visitor.
     144* New: The Inbox search also finds emails and order numbers.
     145* Fix: Saving a visitor's email no longer moves a live chat (Sellsmith Pro) out of the live inbox.
     146* Fix: The privacy eraser removes every chat for an email, not only the first ten, and the export and eraser also find an email saved from the chat.
     147* Fix: The privacy consent note did not show, even when it was turned on.
     148* Fix: Opening a chat from its email link ("Open my chat") stopped the chat from loading.
     149
    140150= 1.1.0 =
    141151* New: Message mode runs the widget with no API key. Visitors leave their question and their email or WhatsApp number, and it is saved as a lead in your inbox for you to follow up. Great for going live in seconds while you set up Gemini.
  • sellsmith/trunk/sellsmith.php

    r3587090 r3712213  
    44 * Plugin URI:        https://devtonicstudios.com/sellsmith
    55 * Description:       An AI sales agent for your store. Answers buyers from your real product data, captures leads, and hands hot buyers to your WhatsApp. Runs on your own free Gemini API key.
    6  * Version:           1.1.0
     6 * Version:           1.2.0
    77 * Requires at least: 6.0
    88 * Requires PHP:      7.4
    … …  
    1717defined( 'ABSPATH' ) || exit;
    1818
    19 define( 'SELLSMITH_VERSION', '1.1.0' );
     19define( 'SELLSMITH_VERSION', '1.2.0' );
    2020define( 'SELLSMITH_DB_VERSION', '1' );
    2121define( 'SELLSMITH_FILE', __FILE__ );
Note: See TracChangeset for help on using the changeset viewer.