Plugin Directory

Changeset 3346700


Ignore:
Timestamp:
08/19/2025 02:06:48 AM (6 months ago)
Author:
infoforte
Message:

Version 1.4
Added content creation feature

Location:
lyxity/trunk
Files:
3 added
12 edited

Legend:

Unmodified
Added
Removed
  • lyxity/trunk/README.md

    r3345835 r3346700  
    9292
    9393## Changelog
     94### 1.4.0
     95- Added content creation feature
    9496
    9597### 1.3.0
  • lyxity/trunk/assets/css/dashboard.css

    r3345835 r3346700  
    11.lyxity-dashboard {
    2     background: #f8f9fa;
    3     padding: 20px;
    4     margin: 20px 20px 0 0;
    5     border-radius: 8px;
     2
     3    background: #fff;
     4    padding: 0;
     5    margin: 0;
     6    border-radius: 0;
    67}
    78
     
    1011    padding-bottom: 20px;
    1112    border-bottom: 1px solid #e9ecef;
     13}
     14
     15/* Dashboard logo sizing (match realtime, keep aspect ratio) */
     16.dashboard-header .dashboard-logo {
     17    height: 56px;
     18    width: auto;
     19    margin-right: 12px;
     20    display: inline-block;
     21    vertical-align: middle;
     22}
     23
     24.dashboard-header .dashboard-logo img {
     25    height: 100%;
     26    width: auto;
     27    display: block;
    1228}
    1329
     
    3551    border-radius: 6px;
    3652    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    37 }
    38 
    39 .date-range-selector .form-control {
    40     width: 160px;
    41 }
    42 
    43 .dashboard-stats {
     53    width: 100%;
     54}
     55
     56/* Ensure date inputs are properly sized on mobile */
     57.date-range-selector input[type="date"] {
     58    min-height: 38px;
     59}
     60
     61/* Date picker styling handled by Bootstrap */
     62
     63/* Dashboard Container Layout */
     64.lyxity-dashboard-container {
     65    display: flex;
     66    flex-wrap: wrap;
     67    gap: 24px;
     68    margin-top: 16px;
     69    margin-bottom: 24px;
     70}
     71
     72.lyxity-dashboard-column {
     73    flex: 1;
     74    min-width: 300px;
     75    background: #fff;
     76    border-radius: 8px;
     77    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
     78    overflow: hidden;
     79}
     80
     81.lyxity-section-header {
     82    background: #f8f9fa;
     83    padding: 12px 16px;
     84    border-bottom: 1px solid #e9ecef;
     85}
     86
     87.lyxity-section-header h2 {
     88    margin: 0;
     89    font-size: 16px;
     90    font-weight: 600;
     91    color: #2c3338;
     92}
     93
     94.lyxity-card-group {
     95    padding: 16px;
    4496    display: grid;
    45     grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    46     gap: 20px;
    47     margin-top: 30px;
    48 }
    49 
    50 .stat-card {
     97    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
     98    gap: 16px;
     99}
     100
     101/* Card Styling */
     102.lyxity-card {
    51103    background: white;
    52     padding: 25px;
    53     border-radius: 8px;
    54     box-shadow: 0 2px 4px rgba(0,0,0,0.08);
     104    padding: 14px 16px;
     105    border-radius: 6px;
     106    box-shadow: 0 1px 2px rgba(0,0,0,0.06);
    55107    transition: transform 0.2s ease;
    56108    position: relative;
    57109    overflow: hidden;
    58 }
    59 
    60 .stat-card:hover {
     110    display: flex;
     111    align-items: center;
     112    min-height: 80px;
     113    height: 100%;
     114}
     115
     116.lyxity-card:hover {
    61117    transform: translateY(-2px);
    62118    box-shadow: 0 4px 8px rgba(0,0,0,0.12);
    63119}
    64120
    65 .stat-card h3 {
    66     font-size: 16px;
    67     color: #50575e;
    68     margin: 0 0 15px 0;
    69     font-weight: 500;
    70 }
    71 
    72 .stat-card .number {
    73     font-size: 36px;
     121.lyxity-card-icon {
     122    margin-right: 12px;
     123    display: flex;
     124    align-items: center;
     125    justify-content: center;
     126    width: 40px;
     127    flex-shrink: 0;
     128}
     129
     130.lyxity-card-icon i,
     131.lyxity-card-icon .dashicons {
     132    font-size: 18px;
     133    color: #b19044;
     134}
     135
     136.lyxity-card-content {
     137    flex: 1;
     138}
     139
     140.lyxity-card-number {
     141    font-size: 24px;
    74142    font-weight: 600;
    75143    color: #2c3338;
    76     margin-bottom: 10px;
    77 }
    78 
    79 .stat-card i {
    80     font-size: 24px;
    81     margin-bottom: 15px;
     144    margin-bottom: 4px;
     145    line-height: 1.2;
     146}
     147
     148.lyxity-card-label {
     149    font-size: 13px;
     150    color: #50575e;
     151    margin: 0 0 2px 0;
     152    font-weight: 500;
     153    line-height: 1.2;
     154}
     155
     156.lyxity-card-description {
     157    font-size: 11px;
     158    color: #6c757d;
     159    margin: 0;
     160    line-height: 1.3;
     161}
     162
     163/* Card Color Styling */
     164.update-card {
     165    border-left: 3px solid #b19044;
     166}
     167
     168.update-card .lyxity-card-icon i {
    82169    color: #b19044;
    83170}
    84171
    85 .stat-card p {
    86     font-size: 13px;
    87     margin: 0;
    88 }
    89 
    90 .update-card {
    91     border-left: 4px solid #b19044;
    92 }
    93 
    94172.enhance-card {
    95     border-left: 4px solid #2271b1;
     173    border-left: 3px solid #2271b1;
     174}
     175
     176.enhance-card .lyxity-card-icon i {
     177    color: #2271b1;
    96178}
    97179
    98180.stale-card {
    99     border-left: 4px solid #dc3545;
    100 }
    101 
    102 .stale-card i {
     181    border-left: 3px solid #dc3545;
     182}
     183
     184.stale-card .lyxity-card-icon i {
    103185    color: #dc3545;
     186}
     187
     188.queued-card {
     189    border-left: 3px solid #b19044;
     190}
     191
     192.processing-card {
     193    border-left: 3px solid #2271b1;
     194}
     195
     196.completed-card {
     197    border-left: 3px solid #00a32a;
     198}
     199
     200.completed-card .lyxity-card-icon .dashicons {
     201    color: #00a32a;
     202}
     203
     204.failed-card {
     205    border-left: 3px solid #dc3545;
     206}
     207
     208.failed-card .lyxity-card-icon .dashicons {
     209    color: #dc3545;
     210}
     211
     212.generated-card {
     213    border-left: 3px solid #6f42c1;
     214}
     215
     216.generated-card .lyxity-card-icon .dashicons {
     217    color: #6f42c1;
     218}
     219
     220.pending-card {
     221    border-left: 3px solid #17a2b8;
     222}
     223
     224.pending-card .lyxity-card-icon .dashicons {
     225    color: #17a2b8;
    104226}
    105227
     
    118240    background-color: #f8f9fa;
    119241    border-right: none;
    120     font-size: 26px;  /* Increased from default to match textbox height */
     242    font-size: 20px;  /* Adjusted for better mobile display */
    121243    padding: 0 10px;  /* Adjusted padding for better alignment */
     244    min-width: 40px;
     245    justify-content: center;
    122246}
    123247
     
    135259}
    136260
     261@media (max-width: 1200px) {
     262    .lyxity-card-group {
     263        grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
     264    }
     265}
     266
     267@media (max-width: 992px) {
     268    .lyxity-dashboard-container {
     269        flex-direction: column;
     270    }
     271   
     272    .lyxity-dashboard-column {
     273        width: 100%;
     274        margin-bottom: 16px;
     275    }
     276   
     277    .lyxity-card-group {
     278        grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
     279    }
     280   
     281    /* Date picker responsiveness handled by Bootstrap */
     282}
     283
    137284@media (max-width: 782px) {
    138285    .lyxity-dashboard {
     
    140287    }
    141288   
    142     .date-range-selector .d-flex {
     289    .dashboard-header {
    143290        flex-direction: column;
    144291    }
    145292   
    146     .date-range-selector .form-control {
    147         margin: 10px 0;
    148     }
    149    
    150     .stat-card {
    151         padding: 20px;
    152     }
    153 }
     293    /* Date picker responsiveness handled by Bootstrap */
     294   
     295    .lyxity-card {
     296        padding: 12px 14px;
     297    }
     298   
     299    .lyxity-card-group {
     300        grid-template-columns: 1fr;
     301    }
     302   
     303    .lyxity-card-number {
     304        font-size: 20px;
     305    }
     306}
     307
     308/* Write Articles Card - Basic Styling */
  • lyxity/trunk/assets/css/settings.css

    r3345835 r3346700  
    55 */
    66
    7 /* Import Libre Baskerville font from Google Fonts */
    8 @import url('https://fonts.googleapis.com/css2?family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&display=swap');
     7/* Clean Bootstrap-based styles */
    98
    10 /* Apply font family only to elements within the settings page container */
    11 .lyxity-settings-wrap h1,
    12 .lyxity-settings-wrap h2,
    13 .lyxity-settings-wrap h3,
    14 .lyxity-settings-wrap h4,
    15 .lyxity-settings-wrap h5,
    16 .lyxity-settings-wrap h6,
    17 .lyxity-settings-wrap p,
    18 .lyxity-settings-wrap input,
    19 .lyxity-settings-wrap button,
    20 .lyxity-settings-wrap select,
    21 .lyxity-settings-wrap textarea {
    22     font-family: 'Libre Baskerville', serif;
     9/* Only minimal custom styling needed */
     10.lyxity-settings-wrap {
     11    margin: 20px 0 0 0;
     12    max-width: 100%;
     13    padding-right: 20px;
    2314}
    2415
    25 .lyxity-settings-wrap {
    26     background-color: #f8f8f5;
    27     /* padding: 20px; */
     16.lyxity-settings-wrap .container-fluid {
    2817    max-width: 100%;
    29     position: relative;
    30     overflow: hidden;
     18    padding-right: 0;
    3119}
    3220
    33 /* Background images */
    34 .lyxity-settings-wrap:before,
    35 .lyxity-settings-wrap:after {
    36     content: "";
    37     position: absolute;
    38     width: 250px;
    39     height: 250px;
    40     background-repeat: no-repeat;
    41     background-size: contain;
    42     opacity: 0.6;
    43     z-index: 0;
     21/* Using Bootstrap mb-4 class */
     22
     23/* No background images - clean UI */
     24
     25/* Using Bootstrap d-flex and align-items-center classes */
     26
     27/* Using Bootstrap img-fluid class */
     28
     29/* Keep gradient text effect but use Bootstrap classes for size and weight */
     30.lyxity-text-logo {
     31    background: linear-gradient(90deg, #4285f4, #34a853);
     32    -webkit-background-clip: text;
     33    background-clip: text;
     34    -webkit-text-fill-color: transparent;
    4435}
    4536
    46 .lyxity-settings-wrap:before {
    47     top: 20px;
    48     left: 20px;
    49     background-image: url('../images/HH_Background.svg');
    50     background-position: top left;
     37/* Using Bootstrap h1 and text-dark classes */
     38
     39
     40/* No background images */
     41
     42/* Using Bootstrap card component instead */
     43#lyxity-settings-form .card {
     44    max-width: none !important;
     45    width: 100%;
     46    border: 1px solid rgba(0,0,0,.125);
    5147}
    5248
    53 .lyxity-settings-wrap:after {
    54     bottom: 20px;
    55     right: 20px;
    56     background-image: url('../images/HH_Background.svg');
    57     background-position: bottom right;
     49#lyxity-settings-form .card-header {
     50    padding-left: 0;
     51    padding-right: 0;
     52    border-left: 0;
     53    border-right: 0;
     54    border-top: 0;
    5855}
    5956
    60 .lyxity-container {
    61     background: white;
    62     padding: 30px;
    63     border-radius: 10px;
    64     box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    65     text-align: center;
    66     /* max-width: 800px; */
    67     margin: 20px;
    68     position: relative;
    69     z-index: 1;
     57/* No background decorations - clean UI */
     58
     59/* Using Bootstrap spacing and typography classes */
     60
     61/* Using Bootstrap card and border utilities */
     62
     63/* Using Bootstrap row and col classes */
     64/* Using Bootstrap img-fluid class */
     65
     66/* Using Bootstrap link classes */
     67
     68/* Using Bootstrap btn and btn-primary classes */
     69
     70/* Using Bootstrap form-group and form-control classes */
     71
     72/* Using Bootstrap img-fluid class */
     73
     74/* Using Bootstrap row and col-md-4 classes */
     75
     76/* Using Bootstrap responsive classes instead of custom media queries */
     77
     78/* Mobile responsive styles */
     79@media (max-width: 782px) {
     80    input.form-control {
     81        font-size: 16px; /* Prevent zoom on iOS */
     82    }
     83   
     84    .lyxity-settings-wrap {
     85        margin: 10px 0 0 0;
     86        padding-right: 10px;
     87    }
     88   
     89    .card-body {
     90        padding: 15px !important;
     91    }
    7092}
    7193
    72 .lyxity-logo {
    73     margin-bottom: 20px;
    74     text-align: center;
     94/* Using native Bootstrap utility classes instead */
     95
     96/* Using Bootstrap form-group and form-label classes */
     97.form-group {
     98    margin-bottom: 5px;
    7599}
    76100
    77 .lyxity-logo svg {
    78     max-width: 180px;
    79     height: auto;
     101.form-label {
     102    display: block;
     103    margin-bottom: 8px;
     104    font-weight: 500;
     105    font-size: 14px;
     106    color: #23282d;
    80107}
    81108
    82 .lyxity-back {
    83     display: block;
    84     text-align: left;
    85     margin-bottom: 15px;
    86     text-decoration: none;
    87     color: #333;
    88     font-family: 'Libre Baskerville', serif;
     109
     110/* Using Bootstrap form-control class */
     111
     112/* Using Bootstrap form-select class */
     113
     114/* Minimal input group styles - using Bootstrap for most styling */
     115
     116
     117/* Minimal button styles - using Bootstrap for most styling */
     118
     119/* Using Bootstrap btn and btn-primary classes instead */
     120
     121/* Using Bootstrap ms-2 class */
     122
     123/* Using Bootstrap hover effects */
     124
     125/* Using Bootstrap spacing and flex utilities */
     126
     127/* Using Bootstrap form-text utility */
     128
     129/* Using Bootstrap alert components instead */
     130
     131/* Using Bootstrap hover effects */
     132.password-control{
     133    width: unset !important;
    89134}
    90 
    91 .lyxity-btn-submit {
    92     background-color: black;
    93     color: white;
    94     width: 100%;
    95     padding: 10px;
    96     border: none;
    97     border-radius: 4px;
    98     cursor: pointer;
    99     font-weight: bold;
    100     font-family: 'Libre Baskerville', serif;
    101     text-transform: uppercase;
    102     letter-spacing: 1px;
     135.dashicons {
     136    vertical-align: middle !important;
    103137}
    104 
    105 .lyxity-btn-submit:hover {
    106     background-color: #333;
    107 }
    108 
    109 .lyxity-form-group {
    110     margin-bottom: 20px;
    111 }
    112 
    113 .lyxity-form-control {
    114     width: 100%;
    115     padding: 10px;
    116     border: 1px solid #ddd;
    117     border-radius: 4px;
    118     font-family: 'Libre Baskerville', serif;
    119 }
    120 
    121 .lyxity-img {
    122     max-width: 100px;
    123     height: auto;
    124     margin: 15px 0;
    125 }
    126 
    127 /* Settings page specific typography */
    128 .h4 {
    129     font-family: 'Libre Baskerville', serif;
    130     font-weight: 700;
    131     margin-bottom: 15px;
    132     font-size: 20px;
    133 }
    134 
    135 p {
    136     font-family: 'Libre Baskerville', serif;
    137     font-size: 14px;
    138     line-height: 1.6;
    139     color: #555;
    140 }
    141 
    142 /* Background image positioning */
    143 .lyxity-bg-top-left,
    144 .lyxity-bg-bottom-right {
    145     position: absolute;
    146     z-index: -1;
    147     pointer-events: none;
    148 }
    149 
    150 .lyxity-bg-top-left {
    151     top: 0;
    152     left: 0;
    153     max-width: 30%;
    154     opacity: 0.7;
    155 }
    156 
    157 .lyxity-bg-bottom-right {
    158     bottom: 0;
    159     right: 0;
    160     max-width: 30%;
    161     opacity: 0.7;
    162 }
    163 
    164 /* Add the CSS from the settings page */
    165 .lyxity-input-group {
    166     display: flex;
    167     align-items: center;
    168     position: relative;
    169 }
    170 
    171 .lyxity-btn {
    172     background: transparent;
    173     border: none;
    174     cursor: pointer;
    175     padding: 8px;
    176     color: #555;
    177     position: absolute;
    178     right: 8px;
    179     top: 50%;
    180     transform: translateY(-50%);
    181     z-index: 10;
    182 }
    183 
    184 .lyxity-btn:hover {
    185     color: #b19044;
    186 }
  • lyxity/trunk/assets/js/admin.js

    r3345835 r3346700  
    11jQuery(document).ready(function($) {
    2     console.log("Lyxity plugin initialized");
    3    
    4     // Debug info for buttons
    5     if ($(".update-single-post").length) {
    6         console.log("Found " + $(".update-single-post").length + " update buttons");
    7     } else {
    8         console.log("No update buttons found on page");
    9     }
    10 
     2   
    113    // Attach click handlers directly
    124    $(document).on("click", ".update-single-post", function(e) {
    135        e.preventDefault();
    14         console.log("Update button clicked");
    156        handleUpdateButtonClick($(this));
    167    });
     
    189    $(document).on("click", ".rewrite-single-post", function(e) {
    1910        e.preventDefault();
    20         console.log("Rewrite button clicked");
    2111        handleRewriteButtonClick($(this));
    2212    });
    2313   
    2414    function handleAjaxError(xhr, status, error, button, type) {
    25         console.error("AJAX request failed:", error);
    26         console.log(xhr.responseText);
    2715        var messages = {
    2816            update: lyxityVars.i18n.updateError,
     
    4432    function handleUpdateButtonClick(button) {
    4533        const postId = button.data("post-id");
    46         console.log("Processing update for post ID: " + postId);
    4734       
    4835        button.attr("disabled", true)
     
    5845            },
    5946            success: function(response) {
    60                 console.log("AJAX response received", response);
    6147                if (response.success) {
    6248                    const data = response.data;
     
    6450                } else {
    6551                    alertify.error(lyxityVars.i18n.updateError);
    66                     console.error("Error response:", response);
    6752                }
    6853               
     
    7863    function handleRewriteButtonClick(button) {
    7964        const postId = button.data("post-id");
    80         console.log("Processing rewrite for post ID: " + postId);
    8165       
    8266        button.attr("disabled", true)
     
    9276            },
    9377            success: function(response) {
    94                 console.log("AJAX response received", response);
    9578                if (response.success) {
    9679                    const data = response.data;
     
    9881                } else {
    9982                    alertify.error(lyxityVars.i18n.rewriteError);
    100                     console.error("Error response:", response);
    10183                }
    10284               
     
    11193   
    11294    function showUpdatePreviewModal(postId, data) {
    113         console.log("Showing update preview modal for post ID: " + postId);
    11495        const modal = $("#update-preview-modal");
    11596       
     
    139120   
    140121    function showRewritePreviewModal(postId, data) {
    141         console.log("Showing rewrite preview modal for post ID: " + postId);
    142122        const modal = $("#rewrite-preview-modal");
    143123       
     
    168148    $(document).on('click', '.modal .btn-close, .modal .btn-secondary[data-bs-dismiss="modal"]', function() {
    169149        const modalEl = $(this).closest('.modal')[0];
    170         console.log('Closing modal with Bootstrap 5:', modalEl.id);
    171150        const modalInstance = bootstrap.Modal.getInstance(modalEl);
    172151        if (modalInstance) {
     
    178157    $(document).on("click", ".apply-update", function(e) {
    179158        e.preventDefault();
    180         console.log("Apply update button clicked");
    181159        const modal = $("#update-preview-modal");
    182160        const postId = modal.data("post-id");
     
    222200    $(document).on("click", ".apply-rewrite", function(e) {
    223201        e.preventDefault();
    224         console.log("Apply rewrite button clicked");
    225202        const modal = $("#rewrite-preview-modal");
    226203        const postId = modal.data("post-id");
  • lyxity/trunk/assets/js/dashboard.js

    r3345835 r3346700  
    5353            },
    5454            success: function(response) {
    55                 console.log(response);
    5655                if(response.result){
    5756                    $('#update-count').text(response.updates);
    5857                    $('#enhance-count').text(response.rewrites);
    59                 } else {
    60                     console.error('API returned false result');
    6158                }
    6259            },
    6360            error: function() {
    64                 console.error('Error calling lyxity_get_dashboard_data_v2');
     61                // Error handled silently
    6562            }
    6663        });
  • lyxity/trunk/assets/js/main-page.js

    r3345835 r3346700  
    4343            function() {
    4444                $.ajax({
    45                     url: ajaxurl,
     45                    url: lyxityVars.ajaxUrl,
    4646                    type: 'POST',
    4747                    data: {
     
    7474            function() {
    7575                $.ajax({
    76                     url: ajaxurl,
     76                    url: lyxityVars.ajaxUrl,
    7777                    type: 'POST',
    7878                    data: {
     
    137137       
    138138        $.ajax({
    139             url: ajaxurl,
     139            url: lyxityVars.ajaxUrl,
    140140            type: 'POST',
    141141            data: {
     
    197197       
    198198        $.ajax({
    199             url: ajaxurl,
     199            url: lyxityVars.ajaxUrl,
    200200            type: 'POST',
    201201            data: {
     
    250250    $(document).on('click', '.modal .btn-close, .modal .btn-secondary[data-bs-dismiss="modal"]', function() {
    251251        const modalEl = $(this).closest('.modal')[0];
    252         console.log('Closing modal with Bootstrap 5:', modalEl.id);
    253252        const modalInstance = bootstrap.Modal.getInstance(modalEl);
    254253        if (modalInstance) {
     
    269268
    270269        $.ajax({
    271             url: ajaxurl,
     270            url: lyxityVars.ajaxUrl,
    272271            type: 'POST',
    273272            data: {
     
    311310
    312311        $.ajax({
    313             url: ajaxurl,
     312            url: lyxityVars.ajaxUrl,
    314313            type: 'POST',
    315314            data: {
     
    333332                    // Redirect to WordPress editor
    334333                    if (response.edit_url) {
    335                         console.log('Redirecting to:', response.edit_url);
    336334                        window.location.href = response.edit_url;
    337                     } else {
    338                         console.error('No edit_url in response');
    339335                    }
    340336                } else {
    341                     console.error('Error in response:', response.message);
    342337                    alertify.error(response.message || lyxityVars.i18n.updateError);
    343338                    button.attr('disabled', false)
     
    364359
    365360        $.ajax({
    366             url: ajaxurl,
     361            url: lyxityVars.ajaxUrl,
    367362            type: 'POST',
    368363            data: {
     
    395390    // Apply rewrite and edit button click
    396391    $(document).on('click', '.apply-rewrite-and-edit', function() {
    397         console.log('Enhance and Edit button clicked');
    398392        const modal = $('#rewrite-preview-modal');
    399393        const postId = modal.data('post-id');
     
    401395        const button = $(this);
    402396       
    403         console.log('Post ID:', postId);
    404         console.log('Content available:', !!content);
    405 
    406397        button.attr('disabled', true)
    407398              .html('<i class="fas fa-spinner fa-spin"></i> ' + lyxityVars.i18n.applying);
    408399
    409400        $.ajax({
    410             url: ajaxurl,
     401            url: lyxityVars.ajaxUrl,
    411402            type: 'POST',
    412403            data: {
     
    417408            },
    418409            success: function(response) {
    419                 console.log('AJAX response received:', response);
    420410                if (response.success) {
    421                     console.log('Success response, edit URL:', response.edit_url);
    422411                    alertify.success('Content enhanced successfully');
    423412                   
     
    431420                    // Redirect to WordPress editor
    432421                    if (response.edit_url) {
    433                         console.log('Redirecting to:', response.edit_url);
    434422                        window.location.href = response.edit_url;
    435423                    } else {
    436                         console.error('No edit_url in response');
    437424                    }
    438425                } else {
    439                     console.error('Error in response:', response.message);
    440426                    alertify.error(response.message || lyxityVars.i18n.updateError);
    441427                    button.attr('disabled', false)
     
    454440    function processUpdates(offset = 0) {
    455441        $.ajax({
    456             url: ajaxurl,
     442            url: lyxityVars.ajaxUrl,
    457443            type: 'POST',
    458444            data: {
     
    484470                    }
    485471                } else {
    486                     console.error('Error:', response.data);
    487472                    alertify.error(lyxityVars.i18n.processingError);
    488473                    $('#start-update').attr('disabled', false);
     
    490475            },
    491476            error: function(xhr, status, error) {
    492                 console.error('AJAX Error:', error);
    493                 console.log(xhr.responseText);
    494477                alertify.error(lyxityVars.i18n.processingErrorDetails);
    495478                $('#start-update').attr('disabled', false);
     
    500483    // Helper function for AJAX errors
    501484    function handleAjaxError(xhr, status, error, button, type) {
    502         console.error("AJAX request failed:", error);
    503         console.log(xhr.responseText);
    504485        var messages = {
    505486            update: lyxityVars.i18n.updateError,
     
    524505    });
    525506
     507    // Write Articles functionality
     508    let suggestedKeywords = [];
     509   
     510    // Handle keyword source selection
     511    function handleKeywordSourceChange() {
     512        const selectedValue = $('input[name="keyword-source"]:checked').val();
     513       
     514        if (selectedValue === 'suggested') {
     515            $('#suggested-keywords-section').show();
     516            $('#manual-keyword-section').hide();
     517            // Auto-fetch keywords when AI Suggested is selected
     518            fetchSuggestedKeywords();
     519        } else if (selectedValue === 'manual') {
     520            $('#suggested-keywords-section').hide();
     521            $('#manual-keyword-section').show();
     522        } else {
     523            $('#suggested-keywords-section').hide();
     524            $('#manual-keyword-section').hide();
     525        }
     526       
     527        // Reset and hide article generation section
     528        $('#article-generation-section').hide();
     529        $('#final-keyword').val('');
     530        $('#write-articles-btn').prop('disabled', true);
     531        $('#metrics-column').hide();
     532    }
     533   
     534    // Store the currently selected keyword text
     535    let currentSelectedKeyword = '';
     536   
     537    // Handle keyword selection from dropdown or manual input
     538    function handleKeywordSelection(keyword) {
     539        if (keyword) {
     540            // Extract keyword text if it's an object
     541            const keywordText = typeof keyword === 'object' && keyword.keyword ? keyword.keyword : keyword;
     542           
     543            // Store the keyword text in our variable
     544            currentSelectedKeyword = keywordText;
     545           
     546            // Show article generation section
     547            document.getElementById('article-generation-section').style.display = 'block';
     548            document.getElementById('write-articles-btn').disabled = false;
     549           
     550            // Show the entire metrics column
     551            document.getElementById('metrics-column').style.display = 'block';
     552           
     553            // Fetch and display keyword metrics
     554            fetchKeywordMetrics(keyword);
     555        } else {
     556            // Clear selected keyword
     557            currentSelectedKeyword = '';
     558           
     559            // Hide article generation section
     560            document.getElementById('article-generation-section').style.display = 'none';
     561            document.getElementById('write-articles-btn').disabled = true;
     562           
     563            // Hide the entire metrics column
     564            document.getElementById('metrics-column').style.display = 'none';
     565        }
     566    }
     567   
     568    // Function to fetch suggested keywords
     569    function fetchSuggestedKeywords() {
     570        const $select = $('#keywords-select');
     571        const $loadingDiv = $('#keywords-loading');
     572        const $errorDiv = $('#keywords-error');
     573       
     574        // Show loading state
     575        $select.html('<option value="">Loading keywords...</option>').prop('disabled', true);
     576        $loadingDiv.show();
     577        $errorDiv.hide();
     578       
     579        $.ajax({
     580            url: lyxityVars.ajaxUrl,
     581            type: 'POST',
     582            data: {
     583                action: 'lyxity_get_suggested_keywords',
     584                nonce: lyxityVars.nonce
     585            },
     586            success: function(response) {
     587               
     588                if (response.success) {
     589                    let keywordsData = response.data.suggestedKeywords;
     590                   
     591                    if (keywordsData && keywordsData.length > 0) {
     592                        suggestedKeywords = keywordsData;
     593                        populateKeywordsDropdown(keywordsData);
     594                        $select.prop('disabled', false);
     595                        $loadingDiv.hide();
     596                    } else {
     597                        showKeywordsError('No keywords found in response');
     598                    }
     599                } else {
     600                    const errorMessage = (response.data && response.data.message) || response.message || 'Failed to fetch keywords';
     601                    showKeywordsError(errorMessage);
     602                }
     603            },
     604            error: function(xhr, status, error) {
     605                showKeywordsError('Error fetching keywords. Please try again.');
     606            },
     607            complete: function() {
     608                $loadingDiv.hide();
     609            }
     610        });
     611    }
     612   
     613    // Function to populate keywords dropdown
     614    function populateKeywordsDropdown(keywords) {
     615        const $select = $('#keywords-select');
     616        $select.empty().append('<option value="">Choose a keyword...</option>');
     617       
     618        keywords.forEach(function(keyword) {
     619            $select.append(`<option value="${keyword.keyword}">${keyword.keyword}</option>`);
     620        });
     621    }
     622   
     623    // Function to show keywords error
     624    function showKeywordsError(message) {
     625        $('#keywords-select').html('<option value="">Error loading keywords</option>').prop('disabled', true);
     626        $('#keywords-error-message').text(message);
     627        $('#keywords-error').show();
     628        $('#keywords-loading').hide();
     629    }
     630   
     631    // Function to fetch and display keyword metrics
     632    function fetchKeywordMetrics(selectedKeyword) {
     633        // Find the keyword data from the suggestedKeywords array
     634        const keywordData = suggestedKeywords.find(k => k.keyword === selectedKeyword);
     635       
     636        if (keywordData) {
     637            // Populate metrics with data from the keyword object
     638            $('#metric-clicks').text(keywordData.clicks || '0');
     639            $('#metric-impressions').text(keywordData.impressions || '0');
     640            $('#metric-position').text(keywordData.position ? parseFloat(keywordData.position).toFixed(1) : '0.0');
     641            $('#metric-ctr').text(keywordData.ctr ? (parseFloat(keywordData.ctr) * 100).toFixed(2) : '0.00');
     642            $('#metric-score').text(keywordData.score || 'N/A');
     643            $('#metric-recommendation').text(keywordData.recommendation || 'No recommendation available');
     644        } else {
     645            // If no data found, show default values
     646            $('#metric-clicks').text('0');
     647            $('#metric-impressions').text('0');
     648            $('#metric-position').text('0.0');
     649            $('#metric-ctr').text('0.00');
     650            $('#metric-score').text('N/A');
     651            $('#metric-recommendation').text('No data available for this keyword');
     652        }
     653    }
     654   
     655    // Add event listeners to radio buttons
     656    $('input[name="keyword-source"]').on('change', handleKeywordSourceChange);
     657   
     658    // Handle dropdown selection
     659    $('#keywords-select').on('change', function() {
     660        const selectedKeyword = $(this).val();
     661        handleKeywordSelection(selectedKeyword);
     662    });
     663   
     664    // Handle manual keyword input
     665    $('#manual-keyword-input').on('input', function() {
     666        const keyword = $(this).val().trim();
     667        handleKeywordSelection(keyword);
     668    });
     669   
     670    // Legacy fetch keywords button click (if still exists)
     671    $('#fetch-keywords-btn').on('click', function() {
     672        const button = $(this);
     673        const originalText = button.html();
     674       
     675        // Show loading state
     676        button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Fetching...');
     677        $('#keywords-loading').show();
     678        $('#keywords-error').hide();
     679        $('#keywords-dropdown-section').hide();
     680       
     681        $.ajax({
     682            url: lyxityVars.ajaxUrl,
     683            type: 'POST',
     684            data: {
     685                action: 'lyxity_get_suggested_keywords',
     686                nonce: lyxityVars.nonce
     687            },
     688            success: function(response) {
     689               
     690                if (response.success) {
     691                    // The API returns data directly with suggestedKeywords at root level
     692                    let keywordsData = response.data.suggestedKeywords;
     693                   
     694                    if (keywordsData && keywordsData.length > 0) {
     695                        suggestedKeywords = keywordsData;
     696                        populateKeywordsDropdown(keywordsData);
     697                        $('#keywords-dropdown-section').show();
     698                        $('#keywords-loading').hide();
     699                       
     700                        // Show success message with keyword count
     701                    } else {
     702                        showKeywordsError('No keywords found in response');
     703                    }
     704                } else {
     705                    const errorMessage = (response.data && response.data.message) || response.message || 'Failed to fetch keywords';
     706                    showKeywordsError(errorMessage);
     707                }
     708            },
     709            error: function(xhr, status, error) {
     710                showKeywordsError('Error fetching keywords. Please try again.');
     711            },
     712            complete: function() {
     713                button.prop('disabled', false).html(originalText);
     714                $('#keywords-loading').hide();
     715            }
     716        });
     717    });
     718   
     719    // Populate keywords dropdown
     720    function populateKeywordsDropdown(keywords) {
     721        const select = $('#keywords-select');
     722        select.empty().append('<option value="">Choose a keyword...</option>');
     723       
     724       
     725        keywords.forEach(function(keyword, index) {
     726            // Create a more informative option text
     727            const optionText = keyword.keyword +
     728                ' (Score: ' + Math.round(keyword.opportunityScore) +
     729                ', Pos: ' + keyword.metrics.averagePosition.toFixed(1) +
     730                ', Clicks: ' + keyword.metrics.totalClicks + ')';
     731           
     732            const option = $('<option></option>')
     733                .attr('value', index)
     734                .text(optionText);
     735            select.append(option);
     736           
     737        });
     738       
     739    }
     740   
     741    // Keyword selection change
     742    $('#keywords-select').on('change', function() {
     743        const selectedIndex = $(this).val();
     744       
     745        if (selectedIndex === '') {
     746            handleKeywordSelection(null); // Clear keyword selection
     747            return;
     748        }
     749       
     750        const selectedKeyword = suggestedKeywords[selectedIndex];
     751        handleKeywordSelection(selectedKeyword); // Use the handleKeywordSelection function
     752       
     753        // Update metrics display
     754        $('#metric-clicks').text(selectedKeyword.metrics.totalClicks.toLocaleString());
     755        $('#metric-impressions').text(selectedKeyword.metrics.totalImpressions.toLocaleString());
     756        $('#metric-position').text(selectedKeyword.metrics.averagePosition.toFixed(1));
     757        $('#metric-ctr').text(selectedKeyword.metrics.averageCtr.toFixed(2));
     758        $('#metric-score').text(Math.round(selectedKeyword.opportunityScore));
     759        $('#metric-recommendation').text(selectedKeyword.recommendation);
     760       
     761        $('#keyword-details').show();
     762    });
     763   
     764    // Write articles button click
     765    $('#write-articles-btn').on('click', function() {
     766        const keywordValue = currentSelectedKeyword; // Use our variable instead of input field
     767        const articleCount = parseInt($('#article-count').val());
     768        const articleStatus = $('#article-status').val();
     769        const ctaLink = $('#cta-link').val().trim();
     770        const ctaCaption = $('#cta-caption').val().trim();
     771       
     772       
     773        if (!keywordValue) {
     774            alertify.error('Please select a keyword first.');
     775            return;
     776        }
     777       
     778        if (!articleCount || articleCount < 1 || articleCount > 10) {
     779            alertify.error('Please enter a valid number of articles (1-10).');
     780            return;
     781        }
     782       
     783        // Validate CTA link and caption - if link is provided but caption is not, show error
     784        if (ctaLink && !ctaCaption) {
     785            alertify.error('Please provide a caption for your Call to Action link.');
     786            return;
     787        }
     788       
     789        // Show confirmation dialog
     790        alertify.confirm(
     791            'Write Articles',
     792            'Are you sure you want to write ' + articleCount + ' article(s) for the keyword "' + keywordValue + '"?',
     793            function() {
     794                // User confirmed - submit data to API
     795                const $button = $('#write-articles-btn');
     796                const originalText = $button.html();
     797               
     798                // Show loading state
     799                $button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Processing...');
     800               
     801                // Get API key from plugin settings
     802                const apiKey = lyxityVars.apiKey || '';
     803               
     804                // Get site URL
     805                const siteUrl = lyxityVars.siteUrl || window.location.origin;
     806               
     807                // Prepare data in the required format
     808                const requestData = {
     809                    Keywords: keywordValue, // Use the keywordValue string directly
     810                    NumberOfArticles: articleCount,
     811                    SiteUrl: siteUrl,
     812                    ClickToActionText: ctaCaption,
     813                    ClickToActionLink: ctaLink,
     814                    ApiKey: apiKey,
     815                    ArticleStatus: articleStatus
     816                };
     817               
     818               
     819                // Make request via WordPress AJAX to proxy through PHP
     820                $.ajax({
     821                    url: lyxityVars.ajaxUrl,
     822                    type: 'POST',
     823                    data: {
     824                        action: 'lyxity_generate_articles',
     825                        nonce: lyxityVars.nonce,
     826                        keywords: requestData.Keywords,
     827                        number_of_articles: requestData.NumberOfArticles,
     828                        site_url: requestData.SiteUrl,
     829                        cta_text: requestData.ClickToActionText,
     830                        cta_link: requestData.ClickToActionLink
     831                    },
     832                    success: function(response) {
     833                        // Show a single white-background message per requirements
     834                       
     835                        alertify.success('Article is being generated in background, you can track progress using Dashboard.');
     836                    },
     837                    error: function(xhr, status, error) {
     838                        alertify.error('Error generating articles: ' + (xhr.responseJSON?.message || error || 'Unknown error'));
     839                    },
     840                    complete: function() {
     841                        // Restore button state
     842                        $button.prop('disabled', false).html(originalText);
     843                    }
     844                });
     845            },
     846            function() {
     847                // User cancelled
     848            }
     849        );
     850    });
     851   
     852    // Show keywords error
     853    function showKeywordsError(message) {
     854        $('#keywords-error-message').text(message);
     855        $('#keywords-error').show();
     856        $('#keywords-dropdown-section').hide();
     857        $('#keywords-loading').hide();
     858    }
     859
    526860    // Update select all checkbox when individual checkboxes change
    527861    $(document).on('change', '.post-checkbox', function() {
  • lyxity/trunk/assets/js/settings.js

    r3345835 r3346700  
    11
    22
     3/**
     4 * Settings page JavaScript
     5 *
     6 * @package Lyxity
     7 */
     8
    39document.addEventListener('DOMContentLoaded', function() {
    4     // Toggle password visibility
     10    // Toggle API key visibility
    511    document.getElementById('toggle-password-visibility').addEventListener('click', function(e) {
    612        e.preventDefault();
     
    1824        }
    1925    });
     26   
     27    // Toggle WordPress password visibility
     28    if (document.getElementById('toggle-wp-password-visibility')) {
     29        document.getElementById('toggle-wp-password-visibility').addEventListener('click', function(e) {
     30            e.preventDefault();
     31            const passwordField = document.getElementById('lyxity_wp_password');
     32            const toggleIcon = this.querySelector('.dashicons');
     33           
     34            if (passwordField.type === 'password') {
     35                passwordField.type = 'text';
     36                toggleIcon.classList.remove('dashicons-visibility');
     37                toggleIcon.classList.add('dashicons-hidden');
     38            } else {
     39                passwordField.type = 'password';
     40                toggleIcon.classList.remove('dashicons-hidden');
     41                toggleIcon.classList.add('dashicons-visibility');
     42            }
     43        });
     44    }
     45   
     46    // Handle settings form submission
     47    const settingsForm = document.getElementById('lyxity-settings-form');
     48    if (settingsForm) {
     49        settingsForm.addEventListener('submit', function(e) {
     50            e.preventDefault();
     51           
     52            // Get form data
     53            const apiKey = document.getElementById('lyxity_api_key').value;
     54            const wpApiUrl = document.getElementById('lyxity_wp_api_url').value;
     55            const wpUsername = document.getElementById('lyxity_wp_username').value;
     56            const wpPassword = document.getElementById('lyxity_wp_password').value;
     57            const articleStatus = document.getElementById('lyxity_article_status').value;
     58            const articleLength = document.getElementById('lyxity_article_length').value;
     59            const defaultLanguage = document.getElementById('lyxity_default_language').value;
     60           
     61            // Get site URL
     62            const siteUrl = window.location.origin;
     63           
     64            // Show loading state
     65            const submitButton = document.getElementById('lyxity-settings-submit');
     66            const originalButtonText = submitButton.innerHTML;
     67            submitButton.disabled = true;
     68            submitButton.innerHTML = '<span class="dashicons dashicons-update-alt"></span> Saving...';
     69           
     70            // Prepare data for API
     71            const data = {
     72                action: 'lyxity_save_settings',
     73                nonce: document.getElementById('lyxity_settings_nonce').value,
     74                api_key: apiKey,
     75                wp_api_url: wpApiUrl,
     76                wp_username: wpUsername,
     77                wp_password: wpPassword,
     78                article_status: articleStatus,
     79                article_length: articleLength,
     80                default_language: defaultLanguage,
     81                site_url: siteUrl
     82            };
     83           
     84            // Send AJAX request
     85            jQuery.post(ajaxurl, data, function(response) {
     86                // Display response
     87                const responseDiv = document.getElementById('settings-response');
     88                responseDiv.style.display = 'block';
     89               
     90                if (response.success) {
     91                    // Ensure we're using success class for successful responses
     92                    responseDiv.className = 'lyxity-form-response error';
     93                    responseDiv.innerHTML = '<p>' + response.data.message + '</p>';
     94                   
     95                } else {
     96                    responseDiv.className = 'lyxity-form-response error';
     97                    responseDiv.innerHTML = '<p>' + response.data.message + '</p>';
     98                }
     99               
     100                // Restore button state
     101                submitButton.innerHTML = originalButtonText;
     102                submitButton.disabled = false;
     103               
     104                // Scroll to response
     105                responseDiv.scrollIntoView({ behavior: 'smooth', block: 'center' });
     106               
     107                // Hide response after 5 seconds
     108                setTimeout(function() {
     109                    if (response.success) {
     110                        jQuery(responseDiv).fadeOut();
     111                    }
     112                }, 5000);
     113            }).fail(function(xhr, status, error) {
     114                // Handle AJAX failure
     115                const responseDiv = document.getElementById('settings-response');
     116                responseDiv.style.display = 'block';
     117                responseDiv.className = 'lyxity-form-response error';
     118                responseDiv.innerHTML = '<p>Error saving settings. Please try again.</p>';
     119               
     120                // Restore button state
     121                submitButton.innerHTML = originalButtonText;
     122                submitButton.disabled = false;
     123               
     124                // Scroll to response
     125                responseDiv.scrollIntoView({ behavior: 'smooth', block: 'center' });
     126            });
     127        });
     128    }
     129   
     130    // Handle API key update button
     131    const updateApiKeyButton = document.getElementById('update-api-key');
     132    if (updateApiKeyButton) {
     133        updateApiKeyButton.addEventListener('click', function(e) {
     134            e.preventDefault();
     135           
     136            const apiKey = document.getElementById('lyxity_api_key').value.trim();
     137           
     138            if (!apiKey) {
     139                alertify.error('Please enter an API key.');
     140                return;
     141            }
     142           
     143            // Show loading state
     144            const originalButtonText = this.innerHTML;
     145            this.disabled = true;
     146            this.innerHTML = '<span class="dashicons dashicons-update-alt"></span> Updating...';
     147           
     148            // Prepare data for AJAX
     149            const data = {
     150                action: 'lyxity_save_api_key',
     151                nonce: document.getElementById('lyxity_settings_nonce').value,
     152                api_key: apiKey
     153            };
     154           
     155            // Send AJAX request
     156            jQuery.post(ajaxurl, data, function(response) {
     157                if (response.success) {
     158                    alertify.success(response.data.message || 'API key updated successfully!');
     159                } else {
     160                    alertify.error(response.data.message || 'Failed to update API key.');
     161                }
     162               
     163                // Restore button state
     164                updateApiKeyButton.innerHTML = originalButtonText;
     165                updateApiKeyButton.disabled = false;
     166            }).fail(function(xhr, status, error) {
     167                alertify.error('Error updating API key. Please try again.');
     168               
     169                // Restore button state
     170                updateApiKeyButton.innerHTML = originalButtonText;
     171                updateApiKeyButton.disabled = false;
     172            });
     173        });
     174    }
     175   
     176    // Add focus effects to form fields
     177    const formControls = document.querySelectorAll('.lyxity-form-control');
     178    formControls.forEach(function(control) {
     179        control.addEventListener('focus', function() {
     180            this.closest('.lyxity-form-group').classList.add('is-focused');
     181        });
     182       
     183        control.addEventListener('blur', function() {
     184            this.closest('.lyxity-form-group').classList.remove('is-focused');
     185        });
     186    });
    20187});
  • lyxity/trunk/includes/class-lyxity-api.php

    r3345835 r3346700  
    1414     * API endpoint
    1515     */
     16
    1617    private $api_endpoint = 'https://lyxity.com/api/';
     18   
    1719    /**
    1820     * Request timeout in seconds
     
    2931    /**
    3032     * Make API request
    31      */
    32     private function make_request($endpoint, $data = array()) {
     33     *
     34     * @param string $endpoint API endpoint
     35     * @param array $data Request data
     36     * @return array|WP_Error Response array or WP_Error on failure
     37     */
     38    public function make_request($endpoint, $data = array()) {
     39           
    3340        $api_key = $this->get_api_key();
    3441        if (empty($api_key)) {
     
    3845        $url = $this->api_endpoint . $endpoint;
    3946
     47        // Determine if we're using a local/dev environment based on the API endpoint
     48        $is_dev_environment = (strpos($this->api_endpoint, 'localhost') !== false || strpos($this->api_endpoint, '127.0.0.1') !== false);
     49       
    4050        $args = array(
    4151            'headers' => array(
     
    5262            'curl_timeout' => $this->timeout
    5363        );
    54 
    55 
     64       
    5665
    5766        $response = wp_remote_post($url, $args);
     
    6372        $status = wp_remote_retrieve_response_code($response);
    6473        $body = wp_remote_retrieve_body($response);
     74       
    6575
    6676        $decoded = json_decode($body, true);
     
    6979            return new WP_Error('json_decode_error', 'Failed to decode API response');
    7080        }
     81
    7182
    7283        return $decoded;
     
    8192     * @return array|WP_Error Response array or WP_Error on failure
    8293     */
     94    /**
     95     * Get site settings from the API
     96     *
     97     * @return array|WP_Error Response array or WP_Error on failure
     98     */
     99    public function get_settings() {
     100       
     101        $site_url = get_site_url();
     102        $api_key = $this->get_api_key();
     103       
     104        $data = array(
     105            'siteUrl' => $site_url,
     106            'apiKey' => $api_key
     107        );
     108       
     109       
     110        $result = $this->make_request('site/getsettings', $data);
     111       
     112       
     113        return $result;
     114    }
     115   
    83116    public function log_content_update($page_url, $appended_content, $update_type = 1) {
    84117
     
    105138
    106139
     140        // Determine if we're using a local/dev environment based on the API endpoint
     141        $is_dev_environment = (strpos($this->api_endpoint, 'localhost') !== false || strpos($this->api_endpoint, '127.0.0.1') !== false);
     142       
    107143        $args = array(
    108144            'method'  => 'POST',
     
    114150            'sslverify' => true
    115151        );
     152       
    116153
    117154        $response = wp_remote_post($endpoint_url, $args);
     
    123160        $response_code = wp_remote_retrieve_response_code($response);
    124161        $body = wp_remote_retrieve_body($response);
     162       
    125163
    126164        $data = json_decode($body, true);
     
    463501
    464502    /**
     503     * Generate articles via Lyxity API
     504     *
     505     * @param array $data Payload including Keys: Keywords, NumberOfArticles, SiteUrl, ClickToActionText, ClickToActionLink
     506     * @return array|WP_Error
     507     */
     508    public function generate_articles($data = array()) {
     509        // Basic validation
     510        if (empty($data['Keywords']) || empty($data['NumberOfArticles'])) {
     511            return new WP_Error('missing_params', 'Keywords and NumberOfArticles are required');
     512        }
     513        // Ensure ApiKey is included in payload as required by API
     514        $api_key = $this->get_api_key();
     515        if (empty($api_key)) {
     516            return new WP_Error('missing_api_key', 'API key is not configured');
     517        }
     518        // API expects 'ApiKey' in the body (note casing)
     519        $data['ApiKey'] = $api_key;
     520        return $this->make_request('articles/generate', $data);
     521    }
     522
     523    /**
     524     * Get real-time status for article generation requests.
     525     *
     526     * Expected $params keys (casing per API):
     527     *  - SiteUrl (required)
     528     *  - RequestId (optional)
     529     *  - PageSize (optional)
     530     *  - Page (optional)
     531     *
     532     * @param array $params
     533     * @return array|WP_Error
     534     */
     535    public function get_realtime_status($params = array()) {
     536       
     537        $api_key = $this->get_api_key();
     538        if (empty($api_key)) {
     539            return new WP_Error('missing_api_key', 'API key is not configured');
     540        }
     541        if (empty($params['SiteUrl'])) {
     542            $params['SiteUrl'] = get_site_url();
     543        }
     544        $payload = array(
     545            'ApiKey' => $api_key,
     546            'SiteUrl' => $params['SiteUrl'],
     547        );
     548        if (!empty($params['RequestId'])) {
     549            $payload['RequestId'] = (int) $params['RequestId'];
     550        }
     551        if (!empty($params['PageSize'])) {
     552            $payload['PageSize'] = max(1, (int) $params['PageSize']);
     553        }
     554        if (!empty($params['Page'])) {
     555            $payload['Page'] = max(1, (int) $params['Page']);
     556        }
     557       
     558       
     559        $result = $this->make_request('articles/realtime', $payload);
     560       
     561       
     562        return $result;
     563    }
     564
     565    /**
    465566     * Update content for a Gutenberg post
    466567     *
     
    577678        return $this->make_request('Dashboard/GetDashboardDataV2', $data);
    578679    }
     680
     681    /**
     682     * Get suggested keywords from the API
     683     *
     684     * @return array|WP_Error Response array or WP_Error on failure
     685     */
     686    public function get_suggested_keywords() {
     687       
     688        $site_url = get_site_url();
     689        $api_key = $this->get_api_key();
     690       
     691       
     692        $data = array(
     693            'siteUrl' => $site_url,
     694            'apiKey' => $api_key
     695        );
     696       
     697       
     698        $result = $this->make_request('articles/suggestkeyword', $data);
     699       
     700       
     701        return $result;
     702    }
    579703}
  • lyxity/trunk/lyxity.php

    r3345835 r3346700  
    33Plugin Name: Lyxity
    44Description: The art of modern search engine optimization
    5 Version: 1.3.0
     5Version: 1.4.0
    66Author: Infoforte
    77Author URI: https://infoforte.com
     
    2020
    2121// Define plugin constants
    22 define('LYXITY_VERSION', '1.3.0');
     22define('LYXITY_VERSION', '1.4.0');
    2323define('LYXITY_FILE', __FILE__);
    2424define('LYXITY_PATH', plugin_dir_path(__FILE__));
    2525define('LYXITY_URL', plugin_dir_url(__FILE__));
    26 define('LYXITY_DEBUG', true); // Set to false in production
     26define('LYXITY_DEBUG', false); // Disabled for production
    2727
    2828// Remove the logger class include
     
    6868        add_action('wp_ajax_lyxity_schedule_bulk_rewrite', array($this, 'ajax_schedule_bulk_rewrite'));
    6969       
     70        // Add AJAX handlers for settings
     71        add_action('wp_ajax_lyxity_save_settings', array($this, 'ajax_save_settings'));
     72        add_action('wp_ajax_lyxity_save_api_key', array($this, 'ajax_save_api_key'));
     73        add_action('wp_ajax_lyxity_get_suggested_keywords', array($this, 'ajax_get_suggested_keywords'));
     74        // New: Generate articles via Lyxity API
     75        add_action('wp_ajax_lyxity_generate_articles', array($this, 'ajax_generate_articles'));
     76        // New: Real-time status proxy
     77        add_action('wp_ajax_lyxity_realtime_status', array($this, 'ajax_realtime_status'));
     78       
    7079        // Add cron hooks
    7180        add_action('lyxity_bulk_update_cron', array($this, 'process_bulk_update'), 10, 1);
     
    101110       
    102111        // Add dashboard-specific styles
     112
     113        if (strpos($hook, 'hazel-harlow-dashboard') !== false || strpos($hook, 'Lyxity-dashboard') !== false) {
     114            wp_enqueue_style('hazel-dashboard', LYXITY_URL . 'assets/css/dashboard.css', array(), LYXITY_VERSION);
     115            // Also load realtime styles on Dashboard (merged view)
     116            wp_enqueue_style('lyxity-realtime-status', LYXITY_URL . 'assets/css/realtime.css', array(), LYXITY_VERSION);
     117        }
     118       
     119        // Add real-time status page styles
     120        if (strpos($hook, 'Lyxity-realtime') !== false) {
     121            wp_enqueue_style('lyxity-realtime-status', LYXITY_URL . 'assets/css/realtime.css', array(), LYXITY_VERSION);
     122        }
     123
    103124        if (strpos($hook, 'lyxity-dashboard') !== false || strpos($hook, 'Lyxity-dashboard') !== false) {
    104125            wp_enqueue_style('lyxity-dashboard', LYXITY_URL . 'assets/css/dashboard.css', array(), LYXITY_VERSION);
     
    115136        wp_register_script('bootstrap-js', LYXITY_URL . 'assets/js/vendor/bootstrap.bundle.min.js', array('jquery', 'popper-js'), '5.3.5', true);
    116137        wp_register_script('alertify-js', LYXITY_URL . 'assets/js/vendor/alertify.min.js', array('jquery'), '1.14.0', true);
     138        wp_register_script('hazel-harlow-admin', LYXITY_URL . 'assets/js/admin.js', array('jquery', 'bootstrap-js', 'alertify-js'), LYXITY_VERSION, true);
     139        wp_register_script('hazel-main-page-js', LYXITY_URL . 'assets/js/main-page.js', array('jquery', 'bootstrap-js', 'alertify-js'), LYXITY_VERSION, true);
     140        // New: Real-time Status page script
     141        wp_register_script('lyxity-realtime-status-js', LYXITY_URL . 'assets/js/realtime-status.js', array('jquery', 'bootstrap-js', 'alertify-js'), LYXITY_VERSION, true);
    117142        wp_register_script('lyxity-admin', LYXITY_URL . 'assets/js/admin.js', array('jquery', 'bootstrap-js', 'alertify-js'), LYXITY_VERSION, true);
    118143        wp_register_script('lyxity-main-page-js', LYXITY_URL . 'assets/js/main-page.js', array('jquery', 'bootstrap-js', 'alertify-js'), LYXITY_VERSION, true);
     
    132157        }
    133158       
     159
     160        if (strpos($hook, 'hazel-harlow-dashboard') !== false || strpos($hook, 'Lyxity-dashboard') !== false) {
     161            wp_enqueue_script('hazel-dashboard-js', LYXITY_URL . 'assets/js/dashboard.js', array('jquery'), LYXITY_VERSION, true);
     162            // Also load realtime script on Dashboard (merged view)
     163            wp_enqueue_script('lyxity-realtime-status-js');
     164        }
     165        // Enqueue real-time status page assets
     166        if (strpos($hook, 'Lyxity-realtime') !== false) {
     167            wp_enqueue_script('lyxity-realtime-status-js');
     168        }
     169
    134170        if (strpos($hook, 'lyxity-dashboard') !== false || strpos($hook, 'Lyxity-dashboard') !== false) {
    135171            wp_enqueue_script('lyxity-dashboard-js', LYXITY_URL . 'assets/js/dashboard.js', array('jquery'), LYXITY_VERSION, true);
     
    178214       
    179215        // Localize all scripts that need these variables
     216
     217        wp_localize_script('hazel-harlow-admin', 'hazelHarlowVars', $localization_array);
     218        wp_localize_script('hazel-main-page-js', 'hazelHarlowVars', $localization_array);
     219        // New: also localize with lyxityVars for consistency across JS files
     220        wp_localize_script('hazel-harlow-admin', 'lyxityVars', $localization_array);
     221        wp_localize_script('hazel-main-page-js', 'lyxityVars', $localization_array);
     222        // Localize real-time status script as well
     223        if (wp_script_is('lyxity-realtime-status-js', 'registered')) {
     224            wp_localize_script('lyxity-realtime-status-js', 'lyxityVars', $localization_array);
     225            wp_localize_script('lyxity-realtime-status-js', 'hazelHarlowVars', $localization_array);
     226        }
     227
    180228        wp_localize_script('lyxity-admin', 'lyxityVars', $localization_array);
    181229        wp_localize_script('lyxity-main-page-js', 'lyxityVars', $localization_array);
     
    187235                'nonce' => wp_create_nonce('lyxity-ajax-nonce')
    188236            ));
    189         }
     237            // Also localize lyxityVars for dashboard
     238            wp_localize_script('hazel-dashboard-js', 'lyxityVars', array(
     239                'ajaxUrl' => esc_url(admin_url('admin-ajax.php')),
     240                'nonce' => wp_create_nonce('lyxity-ajax-nonce')
     241            ));
     242        }
     243        // END enqueue_admin_assets
     244    }
     245
     246    /**
     247     * Render Real-time Status page
     248     */
     249    public function render_realtime_status_page() {
     250        if (!current_user_can('edit_posts')) {
     251            wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'lyxity'));
     252        }
     253        $site_url = esc_url(get_site_url());
     254        ?>
     255        <div class="wrap lyxity-dashboard">
     256            <div class="lyxity-dashboard-header">
     257                <h1 class="wp-heading-inline" style="display:flex;align-items:center;gap:10px;">
     258                    <span class="dashboard-logo">
     259                        <img src="<?php echo esc_url( defined('LYXITY_URL') ? LYXITY_URL . 'assets/images/logo.png' : plugins_url('assets/images/logo.png', __FILE__) ); ?>" alt="Lyxity" />
     260                    </span>
     261                    <?php echo esc_html__('Article Generation Dashboard', 'lyxity'); ?>
     262                </h1>
     263                <div class="lyxity-dashboard-controls">
     264                    <button type="button" class="button button-primary" id="rt-refresh-btn">
     265                        <span class="dashicons dashicons-update" style="font-size:16px;line-height:1;"></span>
     266                        <?php echo esc_html__('Refresh', 'lyxity'); ?>
     267                    </button>
     268                    <label class="lyxity-auto-refresh-toggle">
     269                        <input type="checkbox" id="rt-auto-refresh" />
     270                        <span class="dashicons dashicons-clock" style="font-size:16px;margin-right:4px;"></span>
     271                        <?php echo esc_html__('Auto-refresh', 'lyxity'); ?>
     272                    </label>
     273                </div>
     274            </div>
     275
     276            <!-- System Status Cards -->
     277            <div class="lyxity-status-grid" id="rt-system-status">
     278                <div class="lyxity-status-card lyxity-loading">
     279                    <div class="lyxity-status-icon">
     280                        <span class="dashicons dashicons-clock"></span>
     281                    </div>
     282                    <div class="lyxity-status-content">
     283                        <div class="lyxity-status-number">—</div>
     284                        <div class="lyxity-status-label"><?php echo esc_html__('Queued', 'lyxity'); ?></div>
     285                    </div>
     286                </div>
     287                <div class="lyxity-status-card lyxity-loading">
     288                    <div class="lyxity-status-icon">
     289                        <span class="dashicons dashicons-update-alt"></span>
     290                    </div>
     291                    <div class="lyxity-status-content">
     292                        <div class="lyxity-status-number">—</div>
     293                        <div class="lyxity-status-label"><?php echo esc_html__('Processing', 'lyxity'); ?></div>
     294                    </div>
     295                </div>
     296                <div class="lyxity-status-card lyxity-loading">
     297                    <div class="lyxity-status-icon">
     298                        <span class="dashicons dashicons-yes-alt"></span>
     299                    </div>
     300                    <div class="lyxity-status-content">
     301                        <div class="lyxity-status-number">—</div>
     302                        <div class="lyxity-status-label"><?php echo esc_html__('Completed', 'lyxity'); ?></div>
     303                    </div>
     304                </div>
     305                <div class="lyxity-status-card lyxity-loading">
     306                    <div class="lyxity-status-icon">
     307                        <span class="dashicons dashicons-dismiss"></span>
     308                    </div>
     309                    <div class="lyxity-status-content">
     310                        <div class="lyxity-status-number">—</div>
     311                        <div class="lyxity-status-label"><?php echo esc_html__('Failed', 'lyxity'); ?></div>
     312                    </div>
     313                </div>
     314                <div class="lyxity-status-card lyxity-loading">
     315                    <div class="lyxity-status-icon">
     316                        <span class="dashicons dashicons-edit-page"></span>
     317                    </div>
     318                    <div class="lyxity-status-content">
     319                        <div class="lyxity-status-number">—</div>
     320                        <div class="lyxity-status-label"><?php echo esc_html__('Articles Generated', 'lyxity'); ?></div>
     321                    </div>
     322                </div>
     323                <div class="lyxity-status-card lyxity-loading">
     324                    <div class="lyxity-status-icon">
     325                        <span class="dashicons dashicons-wordpress-alt"></span>
     326                    </div>
     327                    <div class="lyxity-status-content">
     328                        <div class="lyxity-status-number">—</div>
     329                        <div class="lyxity-status-label"><?php echo esc_html__('Pending WP Posts', 'lyxity'); ?></div>
     330                    </div>
     331                </div>
     332            </div>
     333
     334            <!-- Requests Table -->
     335            <div class="lyxity-requests-section">
     336                <div class="lyxity-section-header">
     337                    <h2><?php echo esc_html__('Recent Requests', 'lyxity'); ?></h2>
     338                    <div class="lyxity-pagination-controls">
     339                        <button type="button" class="button" id="rt-prev-btn" disabled>
     340                            <span class="dashicons dashicons-arrow-left-alt2"></span>
     341                        </button>
     342                        <span id="rt-page-indicator" class="lyxity-page-info"><?php echo esc_html__('Page 1 of 1', 'lyxity'); ?></span>
     343                        <button type="button" class="button" id="rt-next-btn" disabled>
     344                            <span class="dashicons dashicons-arrow-right-alt2"></span>
     345                        </button>
     346                    </div>
     347                </div>
     348                <div class="lyxity-table-container">
     349                    <table class="wp-list-table widefat fixed striped" id="rt-requests-table">
     350                        <thead>
     351                            <tr>
     352                                <th class="column-keyword"><?php echo esc_html__('Keyword', 'lyxity'); ?></th>
     353                                <th class="column-created"><?php echo esc_html__('Created', 'lyxity'); ?></th>
     354                                <th class="column-updated"><?php echo esc_html__('Updated', 'lyxity'); ?></th>
     355                                <th class="column-articles"><?php echo esc_html__('Articles', 'lyxity'); ?></th>
     356                                <th class="column-status"><?php echo esc_html__('Status', 'lyxity'); ?></th>
     357                            </tr>
     358                        </thead>
     359                        <tbody>
     360                            <tr class="lyxity-loading-row">
     361                                <td colspan="5">
     362                                    <div class="lyxity-loading-indicator">
     363                                        <span class="spinner is-active"></span>
     364                                        <?php echo esc_html__('Loading requests...', 'lyxity'); ?>
     365                                    </div>
     366                                </td>
     367                            </tr>
     368                        </tbody>
     369                    </table>
     370                </div>
     371            </div>
     372        </div>
     373        <?php
     374    }
     375
     376    /**
     377     * AJAX: Real-time status proxy
     378     */
     379    public function ajax_realtime_status() {
     380        // Security
     381        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'lyxity-ajax-nonce')) {
     382            wp_send_json_error(array('message' => 'Security check failed.'), 403);
     383        }
     384        if (!current_user_can('edit_posts')) {
     385            wp_send_json_error(array('message' => 'Insufficient permissions.'), 403);
     386        }
     387
     388        // Sanitize inputs
     389        $site_url   = isset($_POST['site_url']) ? esc_url_raw(wp_unslash($_POST['site_url'])) : get_site_url();
     390        $request_id = isset($_POST['request_id']) ? absint($_POST['request_id']) : null;
     391        $page_size  = isset($_POST['page_size']) ? max(1, min(100, absint($_POST['page_size']))) : 50;
     392        $page       = isset($_POST['page']) ? max(1, absint($_POST['page'])) : 1;
     393
     394        $params = array(
     395            'SiteUrl' => $site_url,
     396            'PageSize' => $page_size,
     397            'Page' => $page,
     398        );
     399        if (!empty($request_id)) {
     400            $params['RequestId'] = $request_id;
     401        }
     402
     403        $api = new Lyxity_API();
     404        $result = $api->get_realtime_status($params);
     405
     406        if (is_wp_error($result)) {
     407            wp_send_json_error(array('message' => $result->get_error_message()), 500);
     408        }
     409        wp_send_json_success($result);
    190410    }
    191411   
     
    207427            'absint'                       // Use WordPress core function directly
    208428        );
     429       
     430        // Register new settings
     431        register_setting('lyxity-settings-group', 'lyxity_wp_api_url', 'sanitize_text_field');
     432        register_setting('lyxity-settings-group', 'lyxity_wp_username', 'sanitize_text_field');
     433        register_setting('lyxity-settings-group', 'lyxity_wp_password', 'sanitize_text_field');
     434        register_setting('lyxity-settings-group', 'lyxity_article_status', 'sanitize_text_field');
     435        register_setting('lyxity-settings-group', 'lyxity_article_length', 'absint');
     436        register_setting('lyxity-settings-group', 'lyxity_default_language', 'sanitize_text_field');
    209437    }
    210438   
     
    217445    public function sanitize_api_key($input) {
    218446        return sanitize_text_field($input);
     447    }
     448   
     449    /**
     450     * AJAX handler for saving settings to the API
     451     */
     452    public function ajax_save_settings() {
     453        // Check nonce
     454        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'lyxity_settings_nonce')) {
     455            wp_send_json_error(array('message' => 'Security check failed.'));
     456        }
     457       
     458        // Check user capabilities
     459        if (!current_user_can('manage_options')) {
     460            wp_send_json_error(array('message' => 'You do not have permission to perform this action.'));
     461        }
     462       
     463        // Get API key
     464        $api_key = isset($_POST['api_key']) ? sanitize_text_field($_POST['api_key']) : '';
     465       
     466        if (empty($api_key)) {
     467            wp_send_json_error(array('message' => 'API key is required.'));
     468        }
     469       
     470        // Get other settings
     471        $wp_api_url = isset($_POST['wp_api_url']) ? sanitize_text_field($_POST['wp_api_url']) : '';
     472        $wp_username = isset($_POST['wp_username']) ? sanitize_text_field($_POST['wp_username']) : '';
     473        $wp_password = isset($_POST['wp_password']) ? sanitize_text_field($_POST['wp_password']) : '';
     474        $article_status = isset($_POST['article_status']) ? sanitize_text_field($_POST['article_status']) : 'draft';
     475        $article_length = isset($_POST['article_length']) ? absint($_POST['article_length']) : 1500;
     476        $default_language = isset($_POST['default_language']) ? sanitize_text_field($_POST['default_language']) : 'en-GB';
     477        $site_url = isset($_POST['site_url']) ? sanitize_text_field($_POST['site_url']) : get_site_url();
     478       
     479        // Create API instance
     480        $api = new Lyxity_API();
     481       
     482        // Prepare data for API
     483        $data = array(
     484            'apiKey' => $api_key,
     485            'wpApiUrl' => $wp_api_url,
     486            'wpUsername' => $wp_username,
     487            'wpPassword' => $wp_password,
     488            'articleStatus' => $article_status,
     489            'articleLength' => $article_length,
     490            'defaultLanguage' => $default_language,
     491            'siteUrl' => $site_url
     492        );
     493       
     494
     495        // Send to API
     496        $response = $api->make_request('site/updatesettings', $data);
     497       
     498       
     499        if (is_wp_error($response)) {
     500            $error_message = $response->get_error_message();
     501            wp_send_json_error(array('message' => 'API Error: ' . $error_message));
     502        }
     503       
     504        if (!isset($response['result']) || $response['result'] !== true) {
     505            $error_message = isset($response['message']) ? $response['message'] : 'Unknown API error';
     506            wp_send_json_error(array('message' => $error_message));
     507        }
     508       
     509        // Only save API key locally
     510        // update_option('lyxity_api_key', $api_key);
     511        // All other settings are only sent to the API, not stored locally
     512       
     513        wp_send_json_success(array('message' => 'Settings saved successfully.'));
     514    }
     515   
     516    /**
     517     * AJAX handler for saving API key locally
     518     */
     519    public function ajax_save_api_key() {
     520        // Check nonce
     521        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'lyxity_settings_nonce')) {
     522            wp_send_json_error(array('message' => 'Security check failed.'));
     523        }
     524       
     525        // Check user capabilities
     526        if (!current_user_can('manage_options')) {
     527            wp_send_json_error(array('message' => 'You do not have permission to perform this action.'));
     528        }
     529       
     530        // Get API key
     531        $api_key = isset($_POST['api_key']) ? sanitize_text_field($_POST['api_key']) : '';
     532       
     533        if (empty($api_key)) {
     534            wp_send_json_error(array('message' => 'API key is required.'));
     535        }
     536       
     537        // Save API key locally
     538        update_option('lyxity_api_key', $api_key);
     539       
     540        wp_send_json_success(array('message' => 'API key updated successfully.'));
     541    }
     542   
     543    /**
     544     * AJAX handler for fetching suggested keywords
     545     */
     546    public function ajax_get_suggested_keywords() {
     547       
     548        // Check nonce
     549        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'lyxity-ajax-nonce')) {
     550            wp_send_json_error(array('message' => 'Security check failed.'));
     551        }
     552       
     553       
     554        // Check user capabilities
     555        if (!current_user_can('edit_posts')) {
     556            wp_send_json_error(array('message' => 'You do not have permission to perform this action.'));
     557        }
     558       
     559       
     560        // Get API instance and fetch suggested keywords
     561        $api = new Lyxity_API();
     562        $response = $api->get_suggested_keywords();
     563       
     564       
     565        if (is_wp_error($response)) {
     566            wp_send_json_error(array('message' => 'API Error: ' . $response->get_error_message()));
     567        }
     568       
     569       
     570        // For keywords API, check if suggestedKeywords exists instead of success flag
     571        if (!isset($response['suggestedKeywords'])) {
     572            $error_message = isset($response['message']) ? $response['message'] : 'No keywords found in response';
     573            wp_send_json_error(array('message' => $error_message));
     574        }
     575       
     576       
     577        wp_send_json_success($response);
     578    }
     579   
     580    /**
     581     * AJAX handler: Generate articles via Lyxity API
     582     */
     583    public function ajax_generate_articles() {
     584        // Verify nonce
     585        check_ajax_referer('lyxity-ajax-nonce', 'nonce');
     586
     587        // Capability check
     588        if (!current_user_can('edit_posts')) {
     589            wp_send_json_error(array('message' => 'Insufficient permissions'));
     590        }
     591
     592        // Sanitize inputs
     593        $keywords = isset($_POST['keywords']) ? sanitize_text_field(wp_unslash($_POST['keywords'])) : '';
     594        $number_of_articles = isset($_POST['number_of_articles']) ? absint(wp_unslash($_POST['number_of_articles'])) : 0;
     595        $site_url = isset($_POST['site_url']) ? esc_url_raw(wp_unslash($_POST['site_url'])) : get_site_url();
     596        $cta_text = isset($_POST['cta_text']) ? sanitize_text_field(wp_unslash($_POST['cta_text'])) : '';
     597        $cta_link = isset($_POST['cta_link']) ? esc_url_raw(wp_unslash($_POST['cta_link'])) : '';
     598
     599        if (empty($keywords) || $number_of_articles <= 0) {
     600            wp_send_json_error(array('message' => 'Missing or invalid parameters'));
     601        }
     602
     603        // Prepare payload for API (use expected casing)
     604        $payload = array(
     605            'Keywords' => $keywords,
     606            'NumberOfArticles' => $number_of_articles,
     607            'SiteUrl' => $site_url,
     608            'ClickToActionText' => $cta_text,
     609            'ClickToActionLink' => $cta_link,
     610        );
     611
     612        // Call API via centralized class
     613        $api = new Lyxity_API();
     614        $response = $api->generate_articles($payload);
     615
     616        if (is_wp_error($response)) {
     617            wp_send_json_error(array('message' => 'API Error: ' . $response->get_error_message()));
     618        }
     619
     620        wp_send_json_success($response);
    219621    }
    220622   
     
    296698            array($this, 'render_settings_page')
    297699        );
     700
     701        // Real-time Status submenu removed (merged into Dashboard)
    298702    }
    299703   
     
    6081012                <?php endif; ?>
    6091013            </div>
     1014
     1015            <!-- Write Articles Card -->
     1016        <div class="card">
     1017            <h2><?php esc_html_e('Write Articles', 'lyxity'); ?></h2>
     1018           
     1019            <div style="display: flex; gap: 30px; align-items: flex-start;">
     1020                <!-- Left Column: Form -->
     1021                <div style="flex: 1; min-width: 0;">
     1022                    <form id="write-articles-form">
     1023                        <table class="form-table">
     1024                            <tr>
     1025                                <th scope="row"><?php esc_html_e('Keyword Source:', 'lyxity'); ?></th>
     1026                                <td>
     1027                                    <label>
     1028                                        <input type="radio" name="keyword-source" value="suggested" id="suggested-source">
     1029                                        <?php esc_html_e('AI Suggested', 'lyxity'); ?>
     1030                                    </label>
     1031                                    &nbsp;&nbsp;
     1032                                    <label>
     1033                                        <input type="radio" name="keyword-source" value="manual" id="manual-source">
     1034                                        <?php esc_html_e('Manual Entry', 'lyxity'); ?>
     1035                                    </label>
     1036                                </td>
     1037                            </tr>
     1038                        </table>
     1039                       
     1040                        <!-- Suggested Keywords Section -->
     1041                        <div id="suggested-keywords-section" style="display: none;">
     1042                            <?php if (!$api_key_set): ?>
     1043                                <table class="form-table">
     1044                                    <tr>
     1045                                        <th scope="row"></th>
     1046                                        <td>
     1047                                            <p class="description" style="color: #d63638;"><?php esc_html_e('Please set your API key in settings to use this feature', 'lyxity'); ?></p>
     1048                                        </td>
     1049                                    </tr>
     1050                                </table>
     1051                            <?php else: ?>
     1052                                <table class="form-table" id="keywords-dropdown-section">
     1053                                    <tr>
     1054                                        <th scope="row" style="width: 150px;"><label for="keywords-select"><?php esc_html_e('Select Keyword:', 'lyxity'); ?></label></th>
     1055                                        <td>
     1056                                            <select id="keywords-select" style="width: 300px;">
     1057                                                <option value=""><?php esc_html_e('Loading keywords...', 'lyxity'); ?></option>
     1058                                            </select>
     1059                                        </td>
     1060                                    </tr>
     1061                                </table>
     1062                            <?php endif; ?>
     1063                        </div>
     1064                       
     1065                        <!-- Manual Keyword Section -->
     1066                        <div id="manual-keyword-section" style="display: none;">
     1067                            <table class="form-table">
     1068                                <tr>
     1069                                    <th scope="row" style="width: 150px;"><label for="manual-keyword-input"><?php esc_html_e('Enter Keyword:', 'lyxity'); ?></label></th>
     1070                                    <td>
     1071                                        <input type="text" id="manual-keyword-input" class="regular-text" style="width: 300px;" placeholder="<?php esc_attr_e('e.g., best productivity apps', 'lyxity'); ?>">
     1072                                    </td>
     1073                                </tr>
     1074                            </table>
     1075                        </div>
     1076                       
     1077                        <!-- Article Generation Section -->
     1078                        <div id="article-generation-section" style="display: none;">
     1079                            <table class="form-table">
     1080                                <tr>
     1081                                    <th scope="row" style="width: 150px;"><label for="article-count"><?php esc_html_e('Number of Articles:', 'lyxity'); ?></label></th>
     1082                                    <td>
     1083                                        <input type="number" id="article-count" style="width: 300px;" min="1" max="10" value="1">
     1084                                    </td>
     1085                                </tr>
     1086                               
     1087                                <tr>
     1088                                    <th scope="row" style="width: 150px;"><label for="cta-link"><?php esc_html_e('Call to Action Link:', 'lyxity'); ?></label></th>
     1089                                    <td>
     1090                                        <input type="url" id="cta-link" style="width: 300px;" placeholder="<?php esc_attr_e('https://example.com/your-link', 'lyxity'); ?>">
     1091                                        <p class="description"><?php esc_html_e('Optional. Add a call to action link for your articles.', 'lyxity'); ?></p>
     1092                                    </td>
     1093                                </tr>
     1094                                <tr>
     1095                                    <th scope="row" style="width: 150px;"><label for="cta-caption"><?php esc_html_e('CTA Caption:', 'lyxity'); ?></label></th>
     1096                                    <td>
     1097                                        <input type="text" id="cta-caption" style="width: 300px;" placeholder="<?php esc_attr_e('Learn More', 'lyxity'); ?>">
     1098                                        <p class="description"><?php esc_html_e('Optional. Caption text for the call to action link.', 'lyxity'); ?></p>
     1099                                    </td>
     1100                                </tr>
     1101                                <tr>
     1102                                    <th scope="row" style="width: 150px;"></th>
     1103                                    <td>
     1104                                        <button type="button" id="write-articles-btn" class="button button-primary" disabled>
     1105                                            <i class="fas fa-pen"></i> <?php esc_html_e('Write Articles', 'lyxity'); ?>
     1106                                        </button>
     1107                                    </td>
     1108                                </tr>
     1109                            </table>
     1110                        </div>
     1111                       
     1112                        <div id="keywords-loading" style="display: none; text-align: center; padding: 20px;">
     1113                            <i class="fas fa-spinner fa-spin"></i> <?php esc_html_e('Fetching suggested keywords...', 'lyxity'); ?>
     1114                        </div>
     1115                       
     1116                        <div id="keywords-error" style="display: none;" class="notice notice-error">
     1117                            <p id="keywords-error-message"></p>
     1118                        </div>
     1119                    </form>
     1120                </div>
     1121               
     1122                <!-- Right Column: Metrics -->
     1123                <div id="metrics-column" style="flex: 1; min-width: 0; border-left: 1px solid #ddd; padding-left: 30px; display: none;">
     1124                    <div id="keyword-details">
     1125                        <h3 style="margin-top: 0;"><?php esc_html_e('Keyword Metrics', 'lyxity'); ?></h3>
     1126                        <table class="form-table">
     1127                            <tr>
     1128                                <th scope="row"><?php esc_html_e('Performance Data:', 'lyxity'); ?></th>
     1129                                <td>
     1130                                    <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 15px;">
     1131                                        <div>
     1132                                            <strong><?php esc_html_e('Total Clicks:', 'lyxity'); ?></strong><br>
     1133                                            <span id="metric-clicks" style="font-size: 1.2em; color: #0073aa;">-</span>
     1134                                        </div>
     1135                                        <div>
     1136                                            <strong><?php esc_html_e('Total Impressions:', 'lyxity'); ?></strong><br>
     1137                                            <span id="metric-impressions" style="font-size: 1.2em; color: #0073aa;">-</span>
     1138                                        </div>
     1139                                        <div>
     1140                                            <strong><?php esc_html_e('Average Position:', 'lyxity'); ?></strong><br>
     1141                                            <span id="metric-position" style="font-size: 1.2em; color: #0073aa;">-</span>
     1142                                        </div>
     1143                                        <div>
     1144                                            <strong><?php esc_html_e('Average CTR:', 'lyxity'); ?></strong><br>
     1145                                            <span id="metric-ctr" style="font-size: 1.2em; color: #0073aa;">-</span>%
     1146                                        </div>
     1147                                    </div>
     1148                                </td>
     1149                            </tr>
     1150                            <tr>
     1151                                <th scope="row"><?php esc_html_e('Opportunity Score:', 'lyxity'); ?></th>
     1152                                <td>
     1153                                    <span id="metric-score" style="font-size: 1.5em; color: #d63638; font-weight: bold;">-</span>
     1154                                </td>
     1155                            </tr>
     1156                            <tr>
     1157                                <th scope="row"><?php esc_html_e('Recommendation:', 'lyxity'); ?></th>
     1158                                <td>
     1159                                    <p id="metric-recommendation" style="margin: 0; font-style: italic; background: #f0f0f1; padding: 10px; border-radius: 4px;">-</p>
     1160                                </td>
     1161                            </tr>
     1162                        </table>
     1163                    </div>
     1164                </div>
     1165            </div>
    6101166        </div>
     1167       
     1168       
     1169        </div>
     1170       
    6111171       
    6121172        <!-- Update Preview Modal -->
  • lyxity/trunk/readme.txt

    r3345835 r3346700  
    44Requires at least: 5.0
    55Tested up to: 6.8
    6 Stable tag: 1.3.0
     6Stable tag: 1.4.0
    77Requires PHP: 7.0
    88License: GPLv2 or later
     
    8282
    8383== Changelog ==
     84= 1.4.0 =
     85- Added content creation feature
     86
    8487= 1.3.0 =
    8588* Complete WordPress.org compliance update
  • lyxity/trunk/templates/dashboard-page.php

    r3345835 r3346700  
    3030                echo '<span style="font-size: 24px; font-weight: bold; margin-right: 10px;"><span style="color: black;">Lyxity</span></span>';
    3131            }
    32             echo '<span class="wp-heading-inline">' . esc_html__('Analytics Dashboard', 'lyxity') . '</span>';
     32            echo '<span class="wp-heading-inline">' . esc_html__('Dashboard', 'lyxity') . '</span>';
    3333            ?>
    3434        </h1>
    3535       
    3636        <div class="date-range-selector">
    37             <div class="d-flex align-items-center">
    38                 <div class="d-flex align-items-center me-4">
    39                     <label class="date-selector-label" for="start-date"><?php esc_html_e('Start Date', 'lyxity'); ?></label>
    40                     <input type="date" id="start-date" class="form-control">
    41                 </div>
    42                
    43                 <div class="d-flex align-items-center">
    44                     <label class="date-selector-label" for="end-date"><?php esc_html_e('End Date', 'lyxity'); ?></label>
    45                     <input type="date" id="end-date" class="form-control">
     37            <div class="row g-3">
     38                <div class="col-12 col-sm-6 col-lg-4 col-xl-3">
     39                    <div class="d-flex flex-column flex-sm-row align-items-sm-center">
     40                        <label class="date-selector-label me-sm-2 mb-1 mb-sm-0" for="start-date"><?php esc_html_e('Start Date', 'lyxity'); ?></label>
     41                        <input type="date" id="start-date" class="form-control w-100">
     42                    </div>
     43                </div>
     44               
     45                <div class="col-12 col-sm-6 col-lg-4 col-xl-3">
     46                    <div class="d-flex flex-column flex-sm-row align-items-sm-center">
     47                        <label class="date-selector-label me-sm-2 mb-1 mb-sm-0" for="end-date"><?php esc_html_e('End Date', 'lyxity'); ?></label>
     48                        <input type="date" id="end-date" class="form-control w-100">
     49                    </div>
    4650                </div>
    4751            </div>
     
    4953    </div>
    5054
    51     <div class="dashboard-stats">
    52         <div class="stat-card update-card">
    53             <i class="fas fa-sync-alt"></i>
    54             <h3><?php esc_html_e('Content Updates', 'lyxity'); ?></h3>
    55             <div class="number" id="update-count">0</div>
    56             <p class="text-muted"><?php esc_html_e('Total updates in selected period', 'lyxity'); ?></p>
     55    <div class="lyxity-dashboard-container">
     56        <!-- Left Column: Content Updates Section -->
     57        <div class="lyxity-dashboard-column">
     58            <div class="lyxity-section-header">
     59                <h2><?php echo esc_html__('Content Updates', 'lyxity'); ?></h2>
     60            </div>
     61           
     62            <div class="lyxity-card-group">
     63                <!-- Content Updates Card -->
     64                <div class="lyxity-card update-card">
     65                    <div class="lyxity-card-icon">
     66                        <i class="fas fa-sync-alt"></i>
     67                    </div>
     68                    <div class="lyxity-card-content">
     69                        <div class="lyxity-card-number" id="update-count">0</div>
     70                        <div class="lyxity-card-label"><?php esc_html_e('Content Updates', 'lyxity'); ?></div>
     71                        <p class="lyxity-card-description"><?php esc_html_e('Total updates in selected period', 'lyxity'); ?></p>
     72                    </div>
     73                </div>
     74               
     75                <!-- Content Enhancements Card -->
     76                <div class="lyxity-card enhance-card">
     77                    <div class="lyxity-card-icon">
     78                        <i class="fas fa-pen-fancy"></i>
     79                    </div>
     80                    <div class="lyxity-card-content">
     81                        <div class="lyxity-card-number" id="enhance-count">0</div>
     82                        <div class="lyxity-card-label"><?php esc_html_e('Content Enhancements', 'lyxity'); ?></div>
     83                        <p class="lyxity-card-description"><?php esc_html_e('Total enhancements in selected period', 'lyxity'); ?></p>
     84                    </div>
     85                </div>
     86               
     87                <!-- Stale Posts Card -->
     88                <div class="lyxity-card stale-card">
     89                    <div class="lyxity-card-icon">
     90                        <i class="fas fa-clock"></i>
     91                    </div>
     92                    <div class="lyxity-card-content">
     93                        <div class="lyxity-card-number" id="stale-count">1</div>
     94                        <div class="lyxity-card-label"><?php esc_html_e('Stale Posts', 'lyxity'); ?></div>
     95                        <p class="lyxity-card-description"><?php esc_html_e('Posts not updated in 30 days', 'lyxity'); ?></p>
     96                    </div>
     97                </div>
     98            </div>
    5799        </div>
    58100       
    59         <div class="stat-card enhance-card">
    60             <i class="fas fa-pen-fancy"></i>
    61             <h3><?php esc_html_e('Content Enhancements', 'lyxity'); ?></h3>
    62             <div class="number" id="enhance-count">0</div>
    63             <p class="text-muted"><?php esc_html_e('Total enhancements in selected period', 'lyxity'); ?></p>
    64         </div>
     101        <!-- Right Column: Content Creation Section -->
     102        <div class="lyxity-dashboard-column">
     103            <div class="lyxity-section-header">
     104                <h2><?php echo esc_html__('Content Creation', 'lyxity'); ?></h2>
     105            </div>
     106           
     107            <div class="lyxity-card-group">
     108                <!-- Queued Card -->
     109                <div class="lyxity-card queued-card">
     110                    <div class="lyxity-card-icon">
     111                        <span class="dashicons dashicons-clock"></span>
     112                    </div>
     113                    <div class="lyxity-card-content">
     114                        <div class="lyxity-card-number lyxity-status-number">—</div>
     115                        <div class="lyxity-card-label"><?php echo esc_html__('Queued', 'lyxity'); ?></div>
     116                    </div>
     117                </div>
     118               
     119                <!-- Processing Card -->
     120                <div class="lyxity-card processing-card">
     121                    <div class="lyxity-card-icon">
     122                        <span class="dashicons dashicons-update-alt"></span>
     123                    </div>
     124                    <div class="lyxity-card-content">
     125                        <div class="lyxity-card-number lyxity-status-number">—</div>
     126                        <div class="lyxity-card-label"><?php echo esc_html__('Processing', 'lyxity'); ?></div>
     127                    </div>
     128                </div>
     129               
     130                <!-- Completed Card -->
     131                <div class="lyxity-card completed-card">
     132                    <div class="lyxity-card-icon">
     133                        <span class="dashicons dashicons-yes-alt"></span>
     134                    </div>
     135                    <div class="lyxity-card-content">
     136                        <div class="lyxity-card-number lyxity-status-number">—</div>
     137                        <div class="lyxity-card-label"><?php echo esc_html__('Completed', 'lyxity'); ?></div>
     138                    </div>
     139                </div>
     140               
     141                <!-- Failed Card -->
     142                <div class="lyxity-card failed-card">
     143                    <div class="lyxity-card-icon">
     144                        <span class="dashicons dashicons-dismiss"></span>
     145                    </div>
     146                    <div class="lyxity-card-content">
     147                        <div class="lyxity-card-number lyxity-status-number">—</div>
     148                        <div class="lyxity-card-label"><?php echo esc_html__('Failed', 'lyxity'); ?></div>
     149                    </div>
     150                </div>
     151               
     152                <!-- Articles Generated Card -->
     153                <div class="lyxity-card generated-card">
     154                    <div class="lyxity-card-icon">
     155                        <span class="dashicons dashicons-edit-page"></span>
     156                    </div>
     157                    <div class="lyxity-card-content">
     158                        <div class="lyxity-card-number lyxity-status-number">—</div>
     159                        <div class="lyxity-card-label"><?php echo esc_html__('Articles Generated', 'lyxity'); ?></div>
     160                    </div>
     161                </div>
     162               
     163                <!-- Pending WP Posts Card -->
     164                <div class="lyxity-card pending-card">
     165                    <div class="lyxity-card-icon">
     166                        <span class="dashicons dashicons-wordpress-alt"></span>
     167                    </div>
     168                    <div class="lyxity-card-content">
     169                        <div class="lyxity-card-number lyxity-status-number">—</div>
     170                        <div class="lyxity-card-label"><?php echo esc_html__('Pending WP Posts', 'lyxity'); ?></div>
     171                    </div>
     172                </div>
     173            </div>
     174        </div>
     175    </div>
    65176
    66         <div class="stat-card stale-card">
    67             <i class="fas fa-clock"></i>
    68             <h3><?php esc_html_e('Stale Posts', 'lyxity'); ?></h3>
    69             <div class="number" id="stale-count">0</div>
    70             <p class="text-muted"><?php esc_html_e('Posts not updated in 30 days', 'lyxity'); ?></p>
     177    <!-- Requests Table -->
     178    <div class="lyxity-requests-section">
     179        <div class="lyxity-section-header">
     180            <h2><?php echo esc_html__('Recent Requests', 'lyxity'); ?></h2>
     181            <div class="lyxity-pagination-controls">
     182                <button type="button" class="button" id="rt-prev-btn" disabled>
     183                    <span class="dashicons dashicons-arrow-left-alt2"></span>
     184                </button>
     185                <span id="rt-page-indicator" class="lyxity-page-info"><?php echo esc_html__('Page 1 of 1', 'lyxity'); ?></span>
     186                <button type="button" class="button" id="rt-next-btn" disabled>
     187                    <span class="dashicons dashicons-arrow-right-alt2"></span>
     188                </button>
     189            </div>
     190        </div>
     191        <div class="lyxity-table-container">
     192            <table class="wp-list-table widefat fixed striped" id="rt-requests-table">
     193                <thead>
     194                    <tr>
     195                        <th class="column-keyword"><?php echo esc_html__('Keyword', 'lyxity'); ?></th>
     196                        <th class="column-created"><?php echo esc_html__('Created', 'lyxity'); ?></th>
     197                        <th class="column-updated"><?php echo esc_html__('Updated', 'lyxity'); ?></th>
     198                        <th class="column-articles"><?php echo esc_html__('Articles', 'lyxity'); ?></th>
     199                        <th class="column-status"><?php echo esc_html__('Status', 'lyxity'); ?></th>
     200                    </tr>
     201                </thead>
     202                <tbody>
     203                    <tr class="lyxity-loading-row">
     204                        <td colspan="5">
     205                            <div class="lyxity-loading-indicator">
     206                                <span class="spinner is-active"></span>
     207                                <?php echo esc_html__('Loading requests...', 'lyxity'); ?>
     208                            </div>
     209                        </td>
     210                    </tr>
     211                </tbody>
     212            </table>
    71213        </div>
    72214    </div>
  • lyxity/trunk/templates/settings-page.php

    r3345835 r3346700  
    1010}
    1111
     12// Debug output - Only visible in development
     13if (defined('WP_DEBUG') && WP_DEBUG) {
     14    echo '<!-- DEBUG: Settings page template loaded at ' . date('Y-m-d H:i:s') . ' -->';
     15}
     16
    1217// Get the referrer URL for the back button
    1318$referer = wp_get_referer() ? wp_get_referer() : admin_url('admin.php?page=Lyxity');
    1419
     20// Load API key from WordPress settings
     21$api_key = get_option('lyxity_api_key', '');
     22
     23// Initialize default values
     24$wp_api_url = trailingslashit(get_site_url()) . 'wp-json/';
     25$wp_username = '';
     26$article_status = 'draft';
     27$article_length = 1500;
     28$default_language = 'en-GB';
     29
     30// If API key is set, try to load settings from API
     31if (!empty($api_key)) {
     32    // Debug output - Only visible in development
     33    if (defined('WP_DEBUG') && WP_DEBUG) {
     34        error_log('Lyxity: About to call get_settings() with API key present.');
     35    }
     36   
     37    $api = new Lyxity_API();
     38    $settings = $api->get_settings();
     39   
     40    // Debug output - Only visible in development
     41    if (defined('WP_DEBUG') && WP_DEBUG) {
     42        error_log('Lyxity: get_settings() returned: ' . (is_wp_error($settings) ? 'WP_Error' : json_encode($settings)));
     43    }
     44   
     45    if (!is_wp_error($settings) && isset($settings['success']) && $settings['success'] === true && isset($settings['data'])) {
     46        // Extract settings from API response
     47        $data = $settings['data'];
     48       
     49        if (isset($data['wordPressApiUrl'])) {
     50            $wp_api_url = $data['wordPressApiUrl'];
     51        }
     52       
     53        if (isset($data['wordPressUsername'])) {
     54            $wp_username = $data['wordPressUsername'];
     55        }
     56       
     57        if (isset($data['defaultArticleStatus'])) {
     58            $article_status = $data['defaultArticleStatus'];
     59        }
     60       
     61        if (isset($data['articleLength'])) {
     62            $article_length = $data['articleLength'];
     63        }
     64       
     65        if (isset($data['defaultLanguage'])) {
     66            $default_language = $data['defaultLanguage'];
     67        }
     68    }
     69}
     70
    1571// Enqueue the settings page styles - Note: This is not needed anymore, as we handle enqueuing in the main plugin file
    1672?>
    17 <div class="wrap lyxity-settings-wrap">
    18    
    19     <div class="lyxity-container">
    20         <!-- Background images -->
    21     <?php
    22     // Use wp_get_attachment_image if the image is in the media library
    23     // Otherwise, use wp_get_attachment_image_src with a placeholder and style it appropriately
    24     $bg_image_url = esc_url(LYXITY_URL . 'assets/images/HH_Background.svg');
    25     echo '<div class="lyxity-bg-top-left" style="background-image: url(' . esc_url($bg_image_url) . ');"></div>';
    26     echo '<div class="lyxity-bg-bottom-right" style="background-image: url(' . esc_url($bg_image_url) . ');"></div>';
    27     ?>
    28     <div class="lyxity-logo mb-5">
    29             <a href="https://lyxity.com" target="_blank" rel="noopener noreferrer">
    30                 <?php
    31                 // Fix the SVG logo path to use the correct plugin directory
    32                 $logo_path = LYXITY_PATH . 'assets/images/logo.png';
    33                
    34                 if (file_exists($logo_path)) {
    35                     // Get the attachment ID if the image is in the media library
    36                     $attachment_id = attachment_url_to_postid(LYXITY_URL . 'assets/images/logo.png');
     73<div class="wrap lyxity-settings-wrap container-fluid">
     74    <div class="container-fluid p-0" style="max-width: none;">
     75        <div class="row mb-4">
     76           
     77                <div class="card col-12 shadow-sm w-100">
     78                    <div class="card-header bg-white d-flex justify-content-between align-items-center py-3">
     79                        <div class="d-flex align-items-center">
     80                            <a href="https://lyxity.com" target="_blank" rel="noopener noreferrer" class="me-3">
     81                                <?php
     82                                $logo_path = LYXITY_PATH . 'assets/images/logo.png';
     83                               
     84                                if (file_exists($logo_path)) {
     85                                    $attachment_id = attachment_url_to_postid(LYXITY_URL . 'assets/images/logo.png');
     86                                   
     87                                    if ($attachment_id) {
     88                                        echo wp_get_attachment_image($attachment_id, 'full', false, array(
     89                                            'class' => 'img-fluid',
     90                                            'style' => 'max-height: 40px;',
     91                                            'alt' => 'Lyxity'
     92                                        ));
     93                                    } else {
     94                                        echo '<img src="' . esc_url(LYXITY_URL . 'assets/images/logo.png') . '" class="img-fluid" style="max-height: 40px;" alt="Lyxity">';
     95                                    }
     96                                } else {
     97                                    echo '<span class="h4 fw-bold text-primary mb-0">Lyxity</span>';
     98                                }
     99                                ?>
     100                            </a>
     101                            <h1 class="h4 mb-0"><?php esc_html_e('Settings', 'lyxity'); ?></h1>
     102                        </div>
     103                       
     104                    </div>
    37105                   
    38                     if ($attachment_id) {
    39                         // If the image is in the media library, use wp_get_attachment_image
    40                         echo wp_get_attachment_image($attachment_id, 'full', false, array(
    41                             'class' => 'dashboard-logo',
    42                             'style' => 'max-width: 180px; height: auto;',
    43                             'alt' => 'Lyxity'
    44                         ));
    45                     } else {
    46                         // If not in media library, use a div with background image or other WordPress-approved method
    47                         echo '<div class="dashboard-logo" style="background-image: url(' . esc_url(LYXITY_URL . 'assets/images/logo.png') . ');
    48                               background-repeat: no-repeat; background-size: contain; width: 180px; height: 60px;"
    49                               role="img" aria-label="Lyxity"></div>';
    50                     }
    51                 } else {
    52                     // Fallback if SVG file is not found
    53                     echo '<span style="font-size: 24px; font-weight: bold; color: black;">Lyxity</span>';
    54                 }
    55                 ?>
    56             </a>
     106                    <div class="card-body p-4">
     107                        <div class="row mb-4">
     108                            <div class="col-12">
     109                                <h4 class="mb-3"><?php esc_html_e('Set Plugin API Key', 'lyxity'); ?></h4>
     110                                <p class="mb-1"><?php esc_html_e('Welcome to Lyxity.', 'lyxity'); ?></p>
     111                                <p>
     112                                <?php echo wp_kses(
     113                                    __('Please enter your API key below. You can obtain API key from <a href="https://lyxity.com" target="_blank" rel="noopener noreferrer">Here</a>.', 'lyxity'),
     114                                    array(
     115                                        'a' => array(
     116                                            'href' => array(),
     117                                            'target' => array(),
     118                                            'rel' => array()
     119                                        )
     120                                    )
     121                                ); ?>
     122                                </p>
     123                            </div>
     124                        </div>
     125                       
     126                        <?php settings_errors(); ?>
     127                       
     128                        <form id="lyxity-settings-form" method="post">
     129                            <?php wp_nonce_field('lyxity_settings_nonce', 'lyxity_settings_nonce'); ?>
     130                           
     131                            <div class="card mb-4 p-0 border-0 shadow-sm">
     132                                <div class="card-header bg-light">
     133                                    <h5 class="mb-0 ps-3"><?php esc_html_e('API Configuration', 'lyxity'); ?></h5>
     134                                </div>
     135                                <div class="card-body p-4">
     136                                    <div class="row">
     137                                        <div class="col-12">
     138                                            <div class="mb-3">
     139                                                <label for="lyxity_api_key" class="form-label"><?php esc_html_e('API Key', 'lyxity'); ?></label>
     140                                                <div class="input-group mb-3">
     141                                                    <input type="password" id="lyxity_api_key" aria-describedby="toggle-password-visibility" name="lyxity_api_key" class="form-control password-control"
     142                                                        placeholder="<?php esc_attr_e('Enter your API key', 'lyxity'); ?>"
     143                                                        value="<?php echo esc_attr($api_key); ?>">
     144                                                    <button class="btn btn-outline-secondary" type="button" id="toggle-password-visibility" title="<?php esc_attr_e('Show/Hide API key', 'lyxity'); ?>">
     145                                                        <span class="dashicons dashicons-visibility"></span>
     146                                                    </button>
     147                                                   
     148                                                </div>
     149                                                <button class="button button-primary" type="button" id="update-api-key" title="<?php esc_attr_e('Update API Key', 'lyxity'); ?>">
     150                                                        <?php esc_html_e('Update', 'lyxity'); ?>
     151                                                    </button>
     152                                                <div class="form-text">
     153                                                    <?php echo wp_kses(
     154                                                        __('You can obtain an API key from <a href="https://lyxity.com" target="_blank" rel="noopener noreferrer">Lyxity.com</a>', 'lyxity'),
     155                                                        array(
     156                                                            'a' => array(
     157                                                                'href' => array(),
     158                                                                'target' => array(),
     159                                                                'rel' => array()
     160                                                            )
     161                                                        )
     162                                                    ); ?>
     163                                                </div>
     164                                            </div>
     165                                        </div>
     166                                    </div>
     167                                </div>
     168                            </div>
     169                           
     170                            <div class="card mb-4 p-0 border-0 shadow-sm">
     171                                <div class="card-header bg-light">
     172                                    <h5 class="mb-0 ps-3"><?php esc_html_e('WordPress Integration', 'lyxity'); ?></h5>
     173                                </div>
     174                                <div class="card-body p-4">
     175                                    <div class="row g-3">
     176                                        <div class="col-md-4">
     177                                            <div class="mb-3">
     178                                                <label for="lyxity_wp_api_url" class="form-label"><?php esc_html_e('WordPress Base API URL', 'lyxity'); ?></label>
     179                                                <input type="text" id="lyxity_wp_api_url" name="lyxity_wp_api_url" class="form-control"
     180                                                    placeholder="<?php esc_attr_e('WordPress API URL', 'lyxity'); ?>"
     181                                                    value="<?php echo esc_attr($wp_api_url); ?>">
     182                                                <div class="form-text"><?php esc_html_e('Default is auto-detected', 'lyxity'); ?></div>
     183                                            </div>
     184                                        </div>
     185                                       
     186                                        <div class="col-md-4">
     187                                            <div class="mb-3">
     188                                                <label for="lyxity_wp_username" class="form-label"><?php esc_html_e('WordPress Username', 'lyxity'); ?></label>
     189                                                <input type="text" id="lyxity_wp_username" name="lyxity_wp_username" class="form-control"
     190                                                    placeholder="<?php esc_attr_e('WordPress Username', 'lyxity'); ?>"
     191                                                    value="<?php echo esc_attr($wp_username); ?>">
     192                                            </div>
     193                                        </div>
     194                                       
     195                                        <div class="col-md-4">
     196                                            <div class="mb-3">
     197                                                <label for="lyxity_wp_password" class="form-label"><?php esc_html_e('WordPress Password', 'lyxity'); ?></label>
     198                                                <div class="input-group mb-3">
     199                                                    <input type="password" id="lyxity_wp_password" name="lyxity_wp_password" class="form-control password-control" aria-describedby="toggle-wp-password-visibility"
     200                                                        placeholder="<?php esc_attr_e('WordPress Password', 'lyxity'); ?>"
     201                                                        value="">
     202                                                    <button class="btn btn-outline-secondary" type="button" id="toggle-wp-password-visibility" title="<?php esc_attr_e('Show/Hide Password', 'lyxity'); ?>">
     203                                                        <span class="dashicons dashicons-visibility"></span>
     204                                                    </button>
     205                                                </div>
     206                                            </div>
     207                                        </div>
     208                                    </div>
     209                                </div>
     210                            </div>
     211                           
     212                            <div class="card mb-4 p-0 border-0 shadow-sm">
     213                                <div class="card-header bg-light">
     214                                    <h5 class="mb-0 ps-3"><?php esc_html_e('Content Settings', 'lyxity'); ?></h5>
     215                                </div>
     216                                <div class="card-body p-4">
     217                                    <div class="row g-3">
     218                                        <div class="col-md-4">
     219                                            <div class="mb-3">
     220                                                <label for="lyxity_article_status" class="form-label"><?php esc_html_e('Default Article Status', 'lyxity'); ?></label>
     221                                                <select id="lyxity_article_status" name="lyxity_article_status" class="form-control">
     222                                                    <option value="draft" <?php selected($article_status, 'draft'); ?>>
     223                                                        <?php esc_html_e('Draft', 'lyxity'); ?>
     224                                                    </option>
     225                                                    <option value="publish" <?php selected($article_status, 'publish'); ?>>
     226                                                        <?php esc_html_e('Published', 'lyxity'); ?>
     227                                                    </option>
     228                                                </select>
     229                                            </div>
     230                                        </div>
     231                                       
     232                                        <div class="col-md-4">
     233                                            <div class="mb-3">
     234                                                <label for="lyxity_article_length" class="form-label"><?php esc_html_e('Article Length (characters)', 'lyxity'); ?></label>
     235                                                <input type="number" id="lyxity_article_length" name="lyxity_article_length" class="form-control"
     236                                                    min="100" step="100"
     237                                                    value="<?php echo esc_attr($article_length); ?>">
     238                                                <div class="form-text"><?php esc_html_e('Maximum characters for generated articles', 'lyxity'); ?></div>
     239                                            </div>
     240                                        </div>
     241                                       
     242                                        <div class="col-md-4">
     243                                            <div class="mb-3">
     244                                                <label for="lyxity_default_language" class="form-label"><?php esc_html_e('Default Language', 'lyxity'); ?></label>
     245                                                <select id="lyxity_default_language" name="lyxity_default_language" class="form-control">
     246                                                    <option value="en-GB" <?php selected($default_language, 'en-GB'); ?>>
     247                                                        <?php esc_html_e('English (UK)', 'lyxity'); ?>
     248                                                    </option>
     249                                                    <option value="en-US" <?php selected($default_language, 'en-US'); ?>>
     250                                                        <?php esc_html_e('English (US)', 'lyxity'); ?>
     251                                                    </option>
     252                                                    <option value="fr-FR" <?php selected($default_language, 'fr-FR'); ?>>
     253                                                        <?php esc_html_e('French', 'lyxity'); ?>
     254                                                    </option>
     255                                                    <option value="de-DE" <?php selected($default_language, 'de-DE'); ?>>
     256                                                        <?php esc_html_e('German', 'lyxity'); ?>
     257                                                    </option>
     258                                                    <option value="es-ES" <?php selected($default_language, 'es-ES'); ?>>
     259                                                        <?php esc_html_e('Spanish', 'lyxity'); ?>
     260                                                    </option>
     261                                                </select>
     262                                            </div>
     263                                        </div>
     264                                    </div>
     265                                </div>
     266                            </div>
     267                           
     268                            <div id="settings-response" class="alert" style="display: none;"></div>
     269                           
     270                            <div class="d-flex justify-content-start mt-4">
     271                                <button type="submit" id="lyxity-settings-submit" class="button button-primary">
     272                                    <!-- <span class="dashicons dashicons-saved me-2"></span> -->
     273                                    <?php esc_html_e('Save Settings', 'lyxity'); ?>
     274                                </button>
     275                            </div>
     276                        </form>
     277                    </div>
     278                </div>
     279           
    57280        </div>
    58        
    59         <!-- <?php
    60         // This is commented out, but updating to use WordPress functions for future reference
    61         $image_url = LYXITY_URL . 'assets/images/API_Key.png';
    62         $attachment_id = attachment_url_to_postid($image_url);
    63        
    64         if ($attachment_id) {
    65             echo wp_get_attachment_image($attachment_id, 'full', false, array(
    66                 'class' => 'lyxity-img',
    67                 'alt' => 'API Key Setup'
    68             ));
    69         } else {
    70             echo '<div class="lyxity-img" style="background-image: url(' . esc_url($image_url) . ');
    71                   background-repeat: no-repeat; background-size: contain;" role="img" aria-label="API Key Setup"></div>';
    72         }
    73         ?> -->
    74         <h4 class="mb-5"><?php esc_html_e('Set Plugin API Key', 'lyxity'); ?></h4>
    75        
    76         <p class="mb-3">
    77             <?php esc_html_e('Welcome to Lyxity.', 'lyxity'); ?>
    78            
    79         </p>
    80         <p class="mb-3">
    81         <?php echo wp_kses(
    82             __('Please enter your API key below. You can obtain API key from <a href="https://lyxity.com" target="_blank" rel="noopener noreferrer">Here</a>.', 'lyxity'),
    83             array(
    84                 'a' => array(
    85                     'href' => array(),
    86                     'target' => array(),
    87                     'rel' => array()
    88                 )
    89             )
    90         ); ?>
    91             </p>
    92         <?php settings_errors(); ?>
    93        
    94         <form method="post" action="options.php">
    95             <?php settings_fields('lyxity-settings-group'); ?>
    96            
    97             <div class="lyxity-form-group mb-3">
    98                 <div class="lyxity-input-group">
    99                     <input type="password" id="lyxity_api_key" name="lyxity_api_key" class="lyxity-form-control"
    100                            placeholder="<?php esc_attr_e('Enter your API key', 'lyxity'); ?>"
    101                            value="<?php echo esc_attr(get_option('lyxity_api_key')); ?>">
    102                     <button type="button" class="lyxity-btn" id="toggle-password-visibility" title="<?php esc_attr_e('Show/Hide API key', 'lyxity'); ?>">
    103                         <span class="dashicons dashicons-visibility"></span>
    104                     </button>
    105                 </div>
    106             </div>
    107            
    108             <button type="submit" class="lyxity-btn-submit"><?php esc_html_e('SUBMIT', 'lyxity'); ?></button>
    109         </form>
    110281    </div>
    111282</div>
Note: See TracChangeset for help on using the changeset viewer.