Kea2-python 0.3.1__py3-none-any.whl → 0.3.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of Kea2-python might be problematic. Click here for more details.

@@ -211,35 +211,39 @@
211
211
  text-align: left !important;
212
212
  }
213
213
 
214
- /* Enhanced Error Details styling */
215
- .table-custom td:nth-child(7) .collapse {
214
+ /* Enhanced Error Details styling for Property Statistics */
215
+ .table-custom tr.collapse {
216
216
  position: relative;
217
217
  z-index: 10;
218
218
  }
219
219
 
220
- .table-custom td:nth-child(7) .card {
221
- max-width: none;
222
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
220
+ .table-custom tr.collapse td {
221
+ border-top: none;
222
+ padding-top: 0;
223
223
  }
224
224
 
225
- .table-custom td:nth-child(7) .card-body {
226
- padding: 1rem;
227
- max-height: 400px;
228
- overflow-y: auto;
225
+ .table-custom tr.collapse .bg-light {
226
+ max-width: none;
227
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
228
+ border: 1px solid #e9ecef;
229
229
  }
230
230
 
231
- .table-custom td:nth-child(7) pre {
232
- max-height: 200px;
231
+ .table-custom tr.collapse pre {
232
+ max-height: 300px;
233
233
  overflow-y: auto;
234
- font-size: 0.8rem;
234
+ font-size: 0.85rem;
235
235
  line-height: 1.4;
236
+ background-color: #f8f9fa;
237
+ border: 1px solid #e9ecef;
238
+ border-radius: 4px;
239
+ padding: 0.75rem;
236
240
  }
237
241
 
238
- .table-custom td:nth-child(7) details {
242
+ .table-custom tr.collapse details {
239
243
  margin-top: 0.5rem;
240
244
  }
241
245
 
242
- .table-custom td:nth-child(7) summary {
246
+ .table-custom tr.collapse summary {
243
247
  cursor: pointer;
244
248
  margin-bottom: 0.5rem;
245
249
  }
@@ -553,6 +557,25 @@
553
557
  transform: scale(1.2);
554
558
  text-shadow: 0 0 5px rgba(0,0,0,0.6);
555
559
  }
560
+
561
+ /* Specific column widths for property violations table */
562
+ .table-violations th:nth-child(1), .table-violations td:nth-child(1) { /* Index */
563
+ width: 10%;
564
+ min-width: 60px;
565
+ text-align: center;
566
+ }
567
+
568
+ .table-violations th:nth-child(2), .table-violations td:nth-child(2) { /* Property Name */
569
+ width: 40%;
570
+ min-width: 200px;
571
+ text-align: left;
572
+ }
573
+
574
+ .table-violations th:nth-child(3), .table-violations td:nth-child(3) { /* Interaction Scenario Pages */
575
+ width: 50%;
576
+ min-width: 150px;
577
+ text-align: center;
578
+ }
556
579
 
557
580
  @media (max-width: 768px) {
558
581
  .container {
@@ -820,6 +843,117 @@
820
843
  border-color: #d1d5db;
821
844
  }
822
845
 
846
+ /* Property search container with outline */
847
+ .property-search-container {
848
+ position: relative;
849
+ display: flex;
850
+ align-items: center;
851
+ border: 1px solid #d1d5db;
852
+ border-radius: 6px;
853
+ background-color: #f8f9fa;
854
+ transition: all 0.2s ease;
855
+ overflow: hidden;
856
+ }
857
+
858
+ .property-search-container:hover {
859
+ border-color: #9ca3af;
860
+ }
861
+
862
+ .property-search-container:focus-within {
863
+ border-color: #3b82f6;
864
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
865
+ background-color: #ffffff;
866
+ }
867
+
868
+ /* Simple search input styling for Property Statistics */
869
+ .property-stats-search-simple {
870
+ border: none !important;
871
+ box-shadow: none !important;
872
+ background-color: transparent !important;
873
+ border-radius: 0 !important;
874
+ padding: 8px 12px !important;
875
+ font-size: 14px;
876
+ flex: 1;
877
+ outline: none;
878
+ }
879
+
880
+ .property-stats-search-simple:focus {
881
+ background-color: transparent !important;
882
+ box-shadow: none !important;
883
+ border: none !important;
884
+ outline: none;
885
+ }
886
+
887
+ .property-stats-search-simple::placeholder {
888
+ color: #9ca3af;
889
+ font-style: italic;
890
+ }
891
+
892
+ /* Search icon button */
893
+ .property-search-icon-btn {
894
+ background: none;
895
+ border: none;
896
+ padding: 8px 12px;
897
+ color: #6b7280;
898
+ cursor: pointer;
899
+ display: flex;
900
+ align-items: center;
901
+ justify-content: center;
902
+ transition: color 0.2s ease;
903
+ border-left: 1px solid #e5e7eb;
904
+ }
905
+
906
+ .property-search-icon-btn:hover {
907
+ color: #3b82f6;
908
+ background-color: rgba(59, 130, 246, 0.05);
909
+ }
910
+
911
+ .property-search-icon-btn:active {
912
+ color: #1d4ed8;
913
+ }
914
+
915
+ /* Activities Coverage table styling */
916
+ .table-activities thead {
917
+ background-color: #007bff !important;
918
+ color: white;
919
+ }
920
+
921
+ .table-activities .sort-icon {
922
+ color: #ffffff !important;
923
+ text-shadow: 0 0 3px rgba(0,0,0,0.4);
924
+ }
925
+
926
+ .table-activities .sort-icon:hover {
927
+ opacity: 1;
928
+ transform: scale(1.2);
929
+ text-shadow: 0 0 5px rgba(0,0,0,0.6);
930
+ }
931
+
932
+ .table-activities .activities-sort-icon {
933
+ color: #ffffff !important;
934
+ text-shadow: 0 0 3px rgba(0,0,0,0.4);
935
+ }
936
+
937
+ .table-activities .activities-sort-icon:hover {
938
+ opacity: 1;
939
+ transform: scale(1.2);
940
+ text-shadow: 0 0 5px rgba(0,0,0,0.6);
941
+ }
942
+
943
+ /* Column widths for activities table */
944
+ .table-activities th:nth-child(1), .table-activities td:nth-child(1) { /* Activity Name */
945
+ width: 70%;
946
+ min-width: 300px;
947
+ text-align: left;
948
+ padding-left: 180px;
949
+ }
950
+
951
+ .table-activities th:nth-child(2), .table-activities td:nth-child(2) { /* Visit Count */
952
+ width: 30%;
953
+ min-width: 150px;
954
+ text-align: center;
955
+ }
956
+
823
957
  /* Modern Activity Item Styling */
824
958
  .activity-item {
825
959
  background: #ffffff;
@@ -1109,254 +1243,97 @@
1109
1243
  </div>
1110
1244
  </div>
1111
1245
 
1112
- <!-- Tested Activities List -->
1246
+ <!-- Activities Coverage -->
1113
1247
  <div class="section-block">
1114
1248
  <h2 class="section-title">Activities Coverage</h2>
1115
-
1116
- <div class="card">
1117
- <div class="card-header bg-primary text-white">
1118
- <div class="d-flex justify-content-between align-items-center">
1119
- <span><i class="bi bi-app"></i> Activities Coverage Overview</span>
1120
- <span class="badge bg-light text-dark" style="font-size: 1.1em; font-weight: 600;">Coverage: {{ coverage_percent }}%</span>
1249
+
1250
+ <!-- Search Controls for Activities -->
1251
+ <div class="mb-4">
1252
+ <div class="d-flex align-items-center">
1253
+ <div style="width: 33%;">
1254
+ <div class="property-search-container">
1255
+ <input type="text" class="form-control property-search-input property-stats-search-simple"
1256
+ id="activities-search"
1257
+ placeholder="Search activities by name..."
1258
+ data-target="activities-container"
1259
+ data-item-class="activity-row"
1260
+ data-pagination="activities-pagination"
1261
+ data-page-size="activities-page-size">
1262
+ <button class="property-search-icon-btn" type="button" id="activities-search-btn">
1263
+ <i class="bi bi-search"></i>
1264
+ </button>
1265
+ </div>
1121
1266
  </div>
1122
- </div>
1123
- <div class="card-body">
1124
- <div class="alert alert-info mb-3" style="border-left: 4px solid #17a2b8; background-color: #f8f9fa;">
1125
- <small class="text-muted">
1126
- <i class="bi bi-info-circle me-1"></i>
1127
- <strong>Visit Count Explanation:</strong>
1128
- The number after the <i class="bi bi-eye"></i> icon indicates how many times each Activity was visited during testing.
1129
- </small>
1267
+ <div class="ms-3">
1268
+ <small class="text-muted search-results-count" id="activities-search-results"></small>
1130
1269
  </div>
1131
-
1132
- <!-- Navigation Tabs -->
1133
- <ul class="nav nav-tabs mb-3" id="activitiesTabs" role="tablist">
1134
- <li class="nav-item" role="presentation">
1135
- <button class="nav-link active" id="tested-tab" data-bs-toggle="tab"
1136
- data-bs-target="#tested-activities" type="button" role="tab"
1137
- aria-controls="tested-activities" aria-selected="true">
1138
- <i class="bi bi-check-circle"></i> Tested Activities ({{ tested_activities|length }})
1139
- </button>
1140
- </li>
1141
- <li class="nav-item" role="presentation">
1142
- <button class="nav-link" id="all-tab" data-bs-toggle="tab"
1143
- data-bs-target="#all-activities" type="button" role="tab"
1144
- aria-controls="all-activities" aria-selected="false">
1145
- <i class="bi bi-app"></i> All Activities ({{ total_activities|length }})
1146
- </button>
1147
- </li>
1148
- </ul>
1149
-
1150
- <!-- Tab Content -->
1151
- <div class="tab-content" id="activitiesTabContent">
1152
- <!-- Tested Activities Tab -->
1153
- <div class="tab-pane fade show active" id="tested-activities" role="tabpanel"
1154
- aria-labelledby="tested-tab">
1155
- <div class="d-flex justify-content-between align-items-center mb-3">
1156
- <h5 class="mb-0 text-success">
1157
- <i class="bi bi-check-circle-fill"></i> Tested Activities
1158
- </h5>
1159
- <span class="badge bg-success" style="font-size: 1.0em; font-weight: 500;">{{ tested_activities|length }} / {{ total_activities_count }}</span>
1160
- </div>
1161
-
1162
- <!-- Combined Search and Sort Controls for Tested Activities -->
1163
- <div class="search-sort-controls-modern mb-4">
1164
- <div class="d-flex align-items-center gap-4">
1165
- <!-- Search Section -->
1166
- <div class="search-section d-flex align-items-center" style="width: 600px; flex-shrink: 0;">
1167
- <div class="search-icon-wrapper me-3">
1168
- <i class="bi bi-search"></i>
1169
- </div>
1170
- <div class="flex-grow-1">
1171
- <div class="input-group">
1172
- <input type="text" class="form-control activity-search-input"
1173
- id="tested-activity-search"
1174
- placeholder="Search activities..."
1175
- data-target="tested-activities-container"
1176
- data-item-class="tested-activity"
1177
- data-pagination="tested-pagination"
1178
- data-page-size="tested-page-size">
1179
- <button class="btn search-btn" type="button"
1180
- data-target="tested-activity-search">
1181
- <i class="bi bi-search"></i>
1182
- </button>
1183
- <button class="btn btn-outline-secondary search-clear-btn" type="button"
1184
- data-target="tested-activity-search">
1185
- <i class="bi bi-x-lg"></i>
1186
- </button>
1187
- </div>
1188
- <small class="text-muted search-results-count" id="tested-search-results"></small>
1189
- </div>
1190
- </div>
1270
+ </div>
1271
+ </div>
1191
1272
 
1192
- <!-- Sort Section -->
1193
- <div class="sort-section d-flex align-items-center gap-3" style="margin-left: auto;">
1194
- <div class="sort-label d-flex align-items-center gap-2">
1195
- <i class="bi bi-funnel-fill text-muted"></i>
1196
- <span class="text-muted fw-medium">Sort:</span>
1197
- </div>
1198
- <button type="button" class="btn-sort-modern activity-sort-btn" data-sort="traversal" data-order="desc">
1199
- <div class="btn-content">
1200
- <i class="bi bi-eye btn-icon"></i>
1201
- <span class="btn-text">Visit Count</span>
1202
- <i class="bi bi-arrow-down sort-icon btn-arrow"></i>
1203
- </div>
1204
- </button>
1205
- </div>
1206
- </div>
1207
- </div>
1208
-
1209
- <div class="activities-container">
1210
- <div class="activity-list">
1211
- {% if tested_activities|length > 0 %}
1212
- <div id="tested-activities-container">
1213
- {% for activity in tested_activities %}
1214
- <div class="activity-item tested-activity" data-page="1">
1215
- <div class="activity-content">
1216
- <i class="bi bi-check-circle-fill text-success me-2"></i>
1217
- <span class="activity-name">{{ activity }}</span>
1218
- </div>
1219
- {% if activity in activity_count_history %}
1220
- <span class="badge bg-info text-white traversal-badge">
1221
- <i class="bi bi-eye"></i> {{ activity_count_history[activity] }} times
1222
- </span>
1223
- {% endif %}
1224
- </div>
1225
- {% endfor %}
1226
- </div>
1273
+ <div class="table-responsive">
1274
+ <table class="table table-custom table-activities">
1275
+ <thead>
1276
+ <tr>
1277
+ <th>Activity Name <span class="badge bg-primary ms-2" style="font-size: 0.9rem; font-weight: 600;">{{ tested_activities_count }}/{{ total_activities_count }}</span></th>
1278
+ <th>Visit Count <i class="bi bi-arrow-down-up text-muted sort-icon activities-sort-icon" id="visit-count-sort" data-column="visit-count" data-order="none" style="cursor: pointer;"></i></th>
1279
+ </tr>
1280
+ </thead>
1281
+ <tbody id="activities-container">
1282
+ {% if total_activities|length > 0 %}
1283
+ {% for activity in total_activities %}
1284
+ <tr class="activity-row" data-page="1"
1285
+ data-activity-name="{{ activity }}"
1286
+ data-visit-count="{{ activity_count_history[activity] if activity in activity_count_history else 0 }}">
1287
+ <td>
1288
+ {% if activity in tested_activities %}
1289
+ <i class="bi bi-check-circle-fill text-success me-2"></i>
1227
1290
  {% else %}
1228
- <div class="alert alert-warning">
1229
- No tested activities detected
1230
- </div>
1291
+ <i class="bi bi-dash-circle text-secondary me-2"></i>
1231
1292
  {% endif %}
1232
- </div>
1233
- <!-- Pagination for Tested Activities -->
1234
- <div class="pagination-container d-flex justify-content-between align-items-center">
1235
- <div class="d-flex align-items-center">
1236
- <label for="tested-page-size" class="form-label me-2 mb-0">Show:</label>
1237
- <select class="form-select form-select-sm" id="tested-page-size" style="width: auto;">
1238
- <option value="5">5</option>
1239
- <option value="10" selected>10</option>
1240
- <option value="20">20</option>
1241
- <option value="50">50</option>
1242
- <option value="100">100</option>
1243
- </select>
1244
- </div>
1245
- <nav aria-label="Tested Activities Pagination">
1246
- <ul class="pagination pagination-sm mb-0" id="tested-pagination">
1247
- <!-- Pagination will be generated by JavaScript -->
1248
- </ul>
1249
- </nav>
1250
- </div>
1251
- </div>
1252
- </div>
1253
-
1254
- <!-- All Activities Tab -->
1255
- <div class="tab-pane fade" id="all-activities" role="tabpanel"
1256
- aria-labelledby="all-tab">
1257
- <div class="d-flex justify-content-between align-items-center mb-3">
1258
- <h5 class="mb-0 text-primary">
1259
- <i class="bi bi-app"></i> All Activities Overview
1260
- </h5>
1261
- <span class="badge bg-primary" style="font-size: 1.0em; font-weight: 500;">Total: {{ total_activities|length }}</span>
1262
- </div>
1263
-
1264
- <!-- Combined Search and Sort Controls for All Activities -->
1265
- <div class="search-sort-controls-modern mb-4">
1266
- <div class="d-flex align-items-center gap-4">
1267
- <!-- Search Section -->
1268
- <div class="search-section d-flex align-items-center" style="width: 600px; flex-shrink: 0;">
1269
- <div class="search-icon-wrapper me-3">
1270
- <i class="bi bi-search"></i>
1271
- </div>
1272
- <div class="flex-grow-1">
1273
- <div class="input-group">
1274
- <input type="text" class="form-control activity-search-input"
1275
- id="all-activity-search"
1276
- placeholder="Search activities..."
1277
- data-target="all-activities-container"
1278
- data-item-class="all-activity"
1279
- data-pagination="all-pagination"
1280
- data-page-size="all-page-size">
1281
- <button class="btn search-btn" type="button"
1282
- data-target="all-activity-search">
1283
- <i class="bi bi-search"></i>
1284
- </button>
1285
- <button class="btn btn-outline-secondary search-clear-btn" type="button"
1286
- data-target="all-activity-search">
1287
- <i class="bi bi-x-lg"></i>
1288
- </button>
1289
- </div>
1290
- <small class="text-muted search-results-count" id="all-search-results"></small>
1291
- </div>
1292
- </div>
1293
-
1294
- <!-- Sort Section -->
1295
- <div class="sort-section d-flex align-items-center gap-3" style="margin-left: auto;">
1296
- <div class="sort-label d-flex align-items-center gap-2">
1297
- <i class="bi bi-funnel-fill text-muted"></i>
1298
- <span class="text-muted fw-medium">Sort:</span>
1299
- </div>
1300
- <button type="button" class="btn-sort-modern activity-sort-btn" data-sort="traversal" data-order="desc">
1301
- <div class="btn-content">
1302
- <i class="bi bi-eye btn-icon"></i>
1303
- <span class="btn-text">Visit Count</span>
1304
- <i class="bi bi-arrow-down sort-icon btn-arrow"></i>
1305
- </div>
1306
- </button>
1307
- </div>
1308
- </div>
1309
- </div>
1310
-
1311
- <div class="activities-container">
1312
- <div class="activity-list">
1313
- {% if total_activities|length > 0 %}
1314
- <div id="all-activities-container">
1315
- {% for activity in total_activities %}
1316
- <div class="activity-item all-activity" data-page="1">
1317
- <div class="activity-content">
1318
- {% if activity in tested_activities %}
1319
- <i class="bi bi-check-circle-fill text-success me-2"></i>
1320
- {% else %}
1321
- <i class="bi bi-dash-circle text-secondary me-2"></i>
1322
- {% endif %}
1323
- <span class="activity-name">{{ activity }}</span>
1324
- </div>
1325
- {% if activity in activity_count_history %}
1326
- <span class="badge bg-info text-white traversal-badge">
1327
- <i class="bi bi-eye"></i> {{ activity_count_history[activity] }} times
1328
- </span>
1329
- {% endif %}
1330
- </div>
1331
- {% endfor %}
1332
- </div>
1293
+ <span class="activity-name">{{ activity }}</span>
1294
+ </td>
1295
+ <td>
1296
+ {% if activity in activity_count_history %}
1297
+ <span class="badge bg-info text-white">
1298
+ <i class="bi bi-eye"></i> {{ activity_count_history[activity] }} times
1299
+ </span>
1333
1300
  {% else %}
1334
- <div class="alert alert-warning">
1335
- No activities information available
1336
- </div>
1301
+ <span class="badge bg-secondary text-white">
1302
+ <i class="bi bi-dash"></i> 0 times
1303
+ </span>
1337
1304
  {% endif %}
1338
- </div>
1339
- <!-- Pagination for All Activities -->
1340
- <div class="pagination-container d-flex justify-content-between align-items-center">
1341
- <div class="d-flex align-items-center">
1342
- <label for="all-page-size" class="form-label me-2 mb-0">Show:</label>
1343
- <select class="form-select form-select-sm" id="all-page-size" style="width: auto;">
1344
- <option value="5">5</option>
1345
- <option value="10" selected>10</option>
1346
- <option value="20">20</option>
1347
- <option value="50">50</option>
1348
- <option value="100">100</option>
1349
- </select>
1305
+ </td>
1306
+ </tr>
1307
+ {% endfor %}
1308
+ {% else %}
1309
+ <tr>
1310
+ <td colspan="2" class="text-center">
1311
+ <div class="alert alert-warning mb-0">
1312
+ No activities information available
1350
1313
  </div>
1351
- <nav aria-label="All Activities Pagination">
1352
- <ul class="pagination pagination-sm mb-0" id="all-pagination">
1353
- <!-- Pagination will be generated by JavaScript -->
1354
- </ul>
1355
- </nav>
1356
- </div>
1357
- </div>
1358
- </div>
1314
+ </td>
1315
+ </tr>
1316
+ {% endif %}
1317
+ </tbody>
1318
+ </table>
1319
+
1320
+ <!-- Pagination for Activities -->
1321
+ <div class="d-flex justify-content-between align-items-center mt-3">
1322
+ <div class="d-flex align-items-center">
1323
+ <label for="activities-page-size" class="form-label me-2 mb-0">Show:</label>
1324
+ <select class="form-select form-select-sm" id="activities-page-size" style="width: auto;">
1325
+ <option value="5">5</option>
1326
+ <option value="10" selected>10</option>
1327
+ <option value="20">20</option>
1328
+ <option value="50">50</option>
1329
+ <option value="100">100</option>
1330
+ </select>
1359
1331
  </div>
1332
+ <nav aria-label="Activities Pagination">
1333
+ <ul class="pagination pagination-sm mb-0" id="activities-pagination">
1334
+ <!-- Pagination will be generated by JavaScript -->
1335
+ </ul>
1336
+ </nav>
1360
1337
  </div>
1361
1338
  </div>
1362
1339
  </div>
@@ -1518,6 +1495,7 @@
1518
1495
 
1519
1496
  <!-- Property Violations List -->
1520
1497
  {% if take_screenshots %}
1498
+ {% if property_violations %}
1521
1499
  <div class="section-block">
1522
1500
  <h2 class="section-title">Property Violations</h2>
1523
1501
  <div class="table-responsive">
@@ -1526,9 +1504,7 @@
1526
1504
  <tr>
1527
1505
  <th>Index</th>
1528
1506
  <th>Property Name</th>
1529
- <th>Precondition Page</th>
1530
1507
  <th>Interaction Scenario Pages</th>
1531
- <th>Postcondition Page</th>
1532
1508
  </tr>
1533
1509
  </thead>
1534
1510
  <tbody id="property-violations-container">
@@ -1536,14 +1512,12 @@
1536
1512
  <tr class="property-violation-row" data-page="1">
1537
1513
  <td>{{ violation.index }}</td>
1538
1514
  <td><span class="badge bg-light text-dark badge-custom">{{ violation.property_name }}</span></td>
1539
- <td><a href="#{{ violation.precondition_page }}" class="link-button">{{ violation.precondition_page }}</a></td>
1540
1515
  <td><a href="#{{ violation.interaction_pages[0] }}" class="link-button">{{ violation.interaction_pages[0] }} ~ {{ violation.interaction_pages[1] }}</a></td>
1541
- <td><a href="#{{ violation.postcondition_page }}" class="link-button">{{ violation.postcondition_page }}</a></td>
1542
1516
  </tr>
1543
1517
  {% endfor %}
1544
1518
  </tbody>
1545
1519
  </table>
1546
-
1520
+
1547
1521
  <!-- Pagination for Property Violations -->
1548
1522
  <div class="d-flex justify-content-between align-items-center mt-3">
1549
1523
  <div class="d-flex align-items-center">
@@ -1564,6 +1538,14 @@
1564
1538
  </div>
1565
1539
  </div>
1566
1540
  </div>
1541
+ {% else %}
1542
+ <div class="section-block">
1543
+ <h2 class="section-title">Property Violations</h2>
1544
+ <div class="alert alert-info text-center">
1545
+ <i class="bi bi-info-circle"></i> No property violations detected in this test session.
1546
+ </div>
1547
+ </div>
1548
+ {% endif %}
1567
1549
  {% endif %}
1568
1550
 
1569
1551
  <!-- Property Checking Statistics -->
@@ -1571,29 +1553,23 @@
1571
1553
  <h2 class="section-title">Property Checking Statistics</h2>
1572
1554
 
1573
1555
  <!-- Search Controls for Property Statistics -->
1574
- <div class="search-controls-modern mb-4">
1556
+ <div class="mb-4">
1575
1557
  <div class="d-flex align-items-center">
1576
- <div class="search-icon-wrapper me-3">
1577
- <i class="bi bi-search"></i>
1578
- </div>
1579
- <div class="flex-grow-1">
1580
- <div class="input-group">
1581
- <input type="text" class="form-control property-search-input"
1582
- id="property-stats-search"
1583
- placeholder="Search properties by name..."
1558
+ <div style="width: 33%;">
1559
+ <div class="property-search-container">
1560
+ <input type="text" class="form-control property-search-input property-stats-search-simple"
1561
+ id="property-stats-search"
1562
+ placeholder="Search properties by name..."
1584
1563
  data-target="property-stats-container"
1585
1564
  data-item-class="property-stat-row"
1586
1565
  data-pagination="stats-pagination"
1587
1566
  data-page-size="stats-page-size">
1588
- <button class="btn btn-primary search-btn" type="button"
1589
- data-target="property-stats-search">
1567
+ <button class="property-search-icon-btn" type="button" id="property-search-btn">
1590
1568
  <i class="bi bi-search"></i>
1591
1569
  </button>
1592
- <button class="btn btn-outline-secondary search-clear-btn" type="button"
1593
- data-target="property-stats-search">
1594
- <i class="bi bi-x-lg"></i>
1595
- </button>
1596
1570
  </div>
1571
+ </div>
1572
+ <div class="ms-3">
1597
1573
  <small class="text-muted search-results-count" id="property-search-results"></small>
1598
1574
  </div>
1599
1575
  </div>
@@ -1604,11 +1580,11 @@
1604
1580
  <thead>
1605
1581
  <tr>
1606
1582
  <th>Index</th>
1607
- <th>Property Name</th>
1608
- <th>Precondition Satisfied</th>
1609
- <th>Executed</th>
1610
- <th>Fails <i class="bi bi-arrow-down-up text-muted sort-icon" id="fails-sort" data-column="fails" data-order="none" style="cursor: pointer;"></i></th>
1611
- <th>Errors <i class="bi bi-arrow-down-up text-muted sort-icon" id="errors-sort" data-column="errors" data-order="none" style="cursor: pointer;"></i></th>
1583
+ <th>Property Name <span class="badge bg-primary ms-2" style="font-size: 0.9rem; font-weight: 600;">{{ property_stats_summary.total_properties }}</span></th>
1584
+ <th>Precondition Satisfied <span class="badge bg-success ms-2" style="font-size: 0.9rem; font-weight: 600;">{{ property_stats_summary.total_precond_satisfied }}</span></th>
1585
+ <th>Executed <span class="badge bg-info ms-2" style="font-size: 0.9rem; font-weight: 600;">{{ property_stats_summary.total_executed }}</span></th>
1586
+ <th>Fails <span class="badge bg-danger ms-2" style="font-size: 0.9rem; font-weight: 600;">{{ property_stats_summary.total_fails }}</span> <i class="bi bi-arrow-down-up text-muted sort-icon" id="fails-sort" data-column="fails" data-order="none" style="cursor: pointer;"></i></th>
1587
+ <th>Errors <span class="badge bg-warning ms-2" style="font-size: 0.9rem; font-weight: 600;">{{ property_stats_summary.total_errors }}</span> <i class="bi bi-arrow-down-up text-muted sort-icon" id="errors-sort" data-column="errors" data-order="none" style="cursor: pointer;"></i></th>
1612
1588
  <th>Error Details</th>
1613
1589
  </tr>
1614
1590
  </thead>
@@ -1633,127 +1609,140 @@
1633
1609
  {% set property_index = loop.index %}
1634
1610
  {% if error_list|length == 1 %}
1635
1611
  <!-- Single error - simple display -->
1636
- <button class="btn btn-sm btn-outline-danger" type="button" data-bs-toggle="collapse"
1637
- data-bs-target="#single-error-detail-{{ property_index }}" aria-expanded="false"
1612
+ <button class="btn btn-sm btn-outline-danger" type="button" data-bs-toggle="collapse"
1613
+ data-bs-target="#single-error-detail-{{ property_index }}" aria-expanded="false"
1638
1614
  aria-controls="single-error-detail-{{ property_index }}">
1639
1615
  <i class="bi bi-exclamation-triangle"></i> View Error
1640
1616
  </button>
1641
- <div class="collapse mt-2" id="single-error-detail-{{ property_index }}">
1642
- <div class="card card-body bg-light">
1643
- <div class="mb-2">
1644
- <span class="badge bg-{{ 'danger' if error_list[0].state == 'fail' else 'warning' }}">
1645
- {{ error_list[0].state|upper }}
1646
- </span>
1647
- {% if error_list[0].occurrence_count > 1 %}
1648
- <span class="badge bg-info ms-2">
1649
- Occurred {{ error_list[0].occurrence_count }} times
1650
- </span>
1651
- {% endif %}
1652
- {% if error_list[0].startStepsCountList is defined and error_list[0].startStepsCountList|length > 0 %}
1653
- <span class="badge bg-secondary ms-2">
1654
- <i class="bi bi-step-forward"></i> Monkey Steps: {{ error_list[0].startStepsCountList|join(', ') }}
1655
- </span>
1656
- {% endif %}
1657
- </div>
1658
- {% if error_list[0].short_description %}
1659
- <div class="mb-2">
1660
- <strong>Error:</strong> <code>{{ error_list[0].short_description }}</code>
1661
- </div>
1662
- {% endif %}
1663
- <details>
1664
- <summary class="btn btn-sm btn-outline-secondary mb-2">Show Full Traceback</summary>
1665
- <pre class="text-danger mb-0" style="font-size: 0.85rem; white-space: pre-wrap;">{{ error_list[0].traceback }}</pre>
1666
- </details>
1667
- </div>
1668
- </div>
1669
1617
  {% else %}
1670
1618
  <!-- Multiple errors - tabbed display -->
1671
- <button class="btn btn-sm btn-outline-danger" type="button" data-bs-toggle="collapse"
1672
- data-bs-target="#multi-error-detail-{{ property_index }}" aria-expanded="false"
1619
+ <button class="btn btn-sm btn-outline-danger" type="button" data-bs-toggle="collapse"
1620
+ data-bs-target="#multi-error-detail-{{ property_index }}" aria-expanded="false"
1673
1621
  aria-controls="multi-error-detail-{{ property_index }}">
1674
1622
  <i class="bi bi-exclamation-triangle"></i> View {{ error_list|length }} Errors
1675
1623
  </button>
1676
- <div class="collapse mt-2" id="multi-error-detail-{{ property_index }}">
1677
- <div class="card card-body bg-light">
1678
- <!-- Error summary -->
1679
- <div class="mb-3">
1680
- <h6 class="text-danger">Multiple Errors Detected</h6>
1681
- <div class="d-flex flex-wrap gap-1">
1682
- {% for error in error_list %}
1683
- <span class="badge bg-{{ 'danger' if error.state == 'fail' else 'warning' }}">
1684
- {{ error.state|upper }} #{{ loop.index }}
1685
- {% if error.occurrence_count > 1 %} ({{ error.occurrence_count }}x){% endif %}
1686
- {% if error.startStepsCountList is defined and error.startStepsCountList|length > 0 %}
1687
- @{% if error.startStepsCountList|length == 1 %}{{ error.startStepsCountList[0] }}{% else %}{{ error.startStepsCountList[0] }}-{{ error.startStepsCountList[-1] }}{% endif %}
1688
- {% endif %}
1689
- </span>
1690
- {% endfor %}
1691
- </div>
1624
+ {% endif %}
1625
+ {% else %}
1626
+ <span class="text-muted">-</span>
1627
+ {% endif %}
1628
+ </td>
1629
+ </tr>
1630
+ {% if (test_result.fail|default(0) > 0 or test_result.error|default(0) > 0) and property_name in property_error_details %}
1631
+ {% set error_list = property_error_details[property_name] %}
1632
+ {% set property_index = loop.index %}
1633
+ {% if error_list|length == 1 %}
1634
+ <!-- Single error detail row -->
1635
+ <tr class="collapse" id="single-error-detail-{{ property_index }}">
1636
+ <td colspan="7">
1637
+ <div class="bg-light p-3 rounded">
1638
+ <div class="mb-2">
1639
+ <span class="badge bg-{{ 'danger' if error_list[0].state == 'fail' else 'warning' }}">
1640
+ {{ error_list[0].state|upper }}
1641
+ </span>
1642
+ {% if error_list[0].occurrence_count > 1 %}
1643
+ <span class="badge bg-info ms-2">
1644
+ Occurred {{ error_list[0].occurrence_count }} times
1645
+ </span>
1646
+ {% endif %}
1647
+ {% if error_list[0].startStepsCountList is defined and error_list[0].startStepsCountList|length > 0 %}
1648
+ <span class="badge bg-secondary ms-2">
1649
+ <i class="bi bi-step-forward"></i> Monkey Steps: {{ error_list[0].startStepsCountList|join(', ') }}
1650
+ </span>
1651
+ {% endif %}
1652
+ </div>
1653
+ {% if error_list[0].short_description %}
1654
+ <div class="mb-2">
1655
+ <strong>Error:</strong> <code>{{ error_list[0].short_description }}</code>
1692
1656
  </div>
1693
-
1694
- <!-- Error tabs -->
1695
- <ul class="nav nav-pills nav-fill mb-3" id="multi-error-tabs-{{ property_index }}" role="tablist">
1696
- {% for error in error_list %}
1697
- <li class="nav-item" role="presentation">
1698
- <button class="nav-link {{ 'active' if loop.first else '' }} btn-sm"
1699
- id="multi-error-tab-{{ property_index }}-{{ loop.index }}"
1700
- data-bs-toggle="pill"
1701
- data-bs-target="#multi-error-content-{{ property_index }}-{{ loop.index }}"
1702
- type="button" role="tab"
1703
- aria-controls="multi-error-content-{{ property_index }}-{{ loop.index }}"
1704
- aria-selected="{{ 'true' if loop.first else 'false' }}">
1705
- <span class="badge bg-{{ 'danger' if error.state == 'fail' else 'warning' }} me-1">
1706
- {{ error.state|upper }}
1707
- </span>
1708
- #{{ loop.index }}
1709
- </button>
1710
- </li>
1711
- {% endfor %}
1712
- </ul>
1713
-
1714
- <!-- Error content -->
1715
- <div class="tab-content" id="multi-error-content-{{ property_index }}">
1657
+ {% endif %}
1658
+ <details>
1659
+ <summary class="btn btn-sm btn-outline-secondary mb-2">Show Full Traceback</summary>
1660
+ <pre class="text-danger mb-0 text-start" style="font-size: 0.85rem; white-space: pre-wrap; text-align: left;">{{ error_list[0].traceback }}</pre>
1661
+ </details>
1662
+ </div>
1663
+ </td>
1664
+ </tr>
1665
+ {% else %}
1666
+ <!-- Multiple errors detail row -->
1667
+ <tr class="collapse" id="multi-error-detail-{{ property_index }}">
1668
+ <td colspan="7">
1669
+ <div class="bg-light p-3 rounded">
1670
+ <!-- Error summary -->
1671
+ <div class="mb-3">
1672
+ <h6 class="text-danger">Multiple Errors Detected</h6>
1673
+ <div class="d-flex flex-wrap gap-1">
1716
1674
  {% for error in error_list %}
1717
- <div class="tab-pane fade {{ 'show active' if loop.first else '' }}"
1718
- id="multi-error-content-{{ property_index }}-{{ loop.index }}"
1719
- role="tabpanel"
1720
- aria-labelledby="multi-error-tab-{{ property_index }}-{{ loop.index }}">
1721
- <div class="mb-2">
1722
- <span class="badge bg-{{ 'danger' if error.state == 'fail' else 'warning' }}">
1723
- {{ error.state|upper }} #{{ loop.index }}
1724
- </span>
1725
- <small class="text-muted ms-2">Error {{ loop.index }} of {{ loop.length }}</small>
1726
- {% if error.occurrence_count > 1 %}
1727
- <span class="badge bg-info ms-2">
1728
- {{ error.occurrence_count }} occurrences
1729
- </span>
1730
- {% endif %}
1675
+ <span class="badge bg-{{ 'danger' if error.state == 'fail' else 'warning' }}">
1676
+ {{ error.state|upper }} #{{ loop.index }}
1677
+ {% if error.occurrence_count > 1 %} ({{ error.occurrence_count }}x){% endif %}
1731
1678
  {% if error.startStepsCountList is defined and error.startStepsCountList|length > 0 %}
1732
- <span class="badge bg-secondary ms-2">
1733
- <i class="bi bi-step-forward"></i> Monkey Steps: {{ error.startStepsCountList|join(', ') }}
1734
- </span>
1679
+ @{% if error.startStepsCountList|length == 1 %}{{ error.startStepsCountList[0] }}{% else %}{{ error.startStepsCountList[0] }}-{{ error.startStepsCountList[-1] }}{% endif %}
1735
1680
  {% endif %}
1736
- </div>
1737
- {% if error.short_description %}
1738
- <div class="mb-2">
1739
- <strong>Error:</strong> <code>{{ error.short_description }}</code>
1740
- </div>
1681
+ </span>
1682
+ {% endfor %}
1683
+ </div>
1684
+ </div>
1685
+
1686
+ <!-- Error tabs -->
1687
+ <ul class="nav nav-pills nav-fill mb-3" id="multi-error-tabs-{{ property_index }}" role="tablist">
1688
+ {% for error in error_list %}
1689
+ <li class="nav-item" role="presentation">
1690
+ <button class="nav-link {{ 'active' if loop.first else '' }} btn-sm"
1691
+ id="multi-error-tab-{{ property_index }}-{{ loop.index }}"
1692
+ data-bs-toggle="pill"
1693
+ data-bs-target="#multi-error-content-{{ property_index }}-{{ loop.index }}"
1694
+ type="button" role="tab"
1695
+ aria-controls="multi-error-content-{{ property_index }}-{{ loop.index }}"
1696
+ aria-selected="{{ 'true' if loop.first else 'false' }}">
1697
+ <span class="badge bg-{{ 'danger' if error.state == 'fail' else 'warning' }} me-1">
1698
+ {{ error.state|upper }}
1699
+ </span>
1700
+ #{{ loop.index }}
1701
+ </button>
1702
+ </li>
1703
+ {% endfor %}
1704
+ </ul>
1705
+
1706
+ <!-- Error content -->
1707
+ <div class="tab-content" id="multi-error-content-{{ property_index }}">
1708
+ {% for error in error_list %}
1709
+ <div class="tab-pane fade {{ 'show active' if loop.first else '' }}"
1710
+ id="multi-error-content-{{ property_index }}-{{ loop.index }}"
1711
+ role="tabpanel"
1712
+ aria-labelledby="multi-error-tab-{{ property_index }}-{{ loop.index }}">
1713
+ <div class="mb-2">
1714
+ <span class="badge bg-{{ 'danger' if error.state == 'fail' else 'warning' }}">
1715
+ {{ error.state|upper }} #{{ loop.index }}
1716
+ </span>
1717
+ <small class="text-muted ms-2">Error {{ loop.index }} of {{ loop.length }}</small>
1718
+ {% if error.occurrence_count > 1 %}
1719
+ <span class="badge bg-info ms-2">
1720
+ {{ error.occurrence_count }} occurrences
1721
+ </span>
1722
+ {% endif %}
1723
+ {% if error.startStepsCountList is defined and error.startStepsCountList|length > 0 %}
1724
+ <span class="badge bg-secondary ms-2">
1725
+ <i class="bi bi-step-forward"></i> Monkey Steps: {{ error.startStepsCountList|join(', ') }}
1726
+ </span>
1741
1727
  {% endif %}
1742
- <details>
1743
- <summary class="btn btn-sm btn-outline-secondary mb-2">Show Full Traceback</summary>
1744
- <pre class="text-danger mb-0" style="font-size: 0.85rem; white-space: pre-wrap;">{{ error.traceback }}</pre>
1745
- </details>
1746
1728
  </div>
1747
- {% endfor %}
1729
+ {% if error.short_description %}
1730
+ <div class="mb-2">
1731
+ <strong>Error:</strong> <code>{{ error.short_description }}</code>
1732
+ </div>
1733
+ {% endif %}
1734
+ <details>
1735
+ <summary class="btn btn-sm btn-outline-secondary mb-2">Show Full Traceback</summary>
1736
+ <pre class="text-danger mb-0 text-start" style="font-size: 0.85rem; white-space: pre-wrap; text-align: left;">{{ error.traceback }}</pre>
1737
+ </details>
1748
1738
  </div>
1739
+ {% endfor %}
1749
1740
  </div>
1750
1741
  </div>
1751
- {% endif %}
1752
- {% else %}
1753
- <span class="text-muted">-</span>
1754
- {% endif %}
1755
- </td>
1756
- </tr>
1742
+ </td>
1743
+ </tr>
1744
+ {% endif %}
1745
+ {% endif %}
1757
1746
  {% endfor %}
1758
1747
  </tbody>
1759
1748
  </table>
@@ -2097,16 +2086,18 @@
2097
2086
  // Initialize crash events functionality
2098
2087
  initCrashAnalysis();
2099
2088
 
2100
- // Initialize pagination for Activities lists
2101
- initPagination('tested-activities-container', 'tested-activity', 'tested-pagination', 'tested-page-size');
2102
- initPagination('all-activities-container', 'all-activity', 'all-pagination', 'all-page-size');
2103
-
2089
+ // Initialize pagination for Activities table
2090
+ initPagination('activities-container', 'activity-row', 'activities-pagination', 'activities-page-size');
2091
+
2104
2092
  // Initialize activity sorting
2105
- initActivitySorting();
2106
-
2093
+ initActivitiesSorting();
2094
+
2107
2095
  // Initialize activity searching
2108
- initActivitySearching();
2109
-
2096
+ initActivitiesSearching();
2097
+
2098
+ // Initialize activities page size selector
2099
+ initActivitiesPageSize();
2100
+
2110
2101
  // Initialize property statistics searching
2111
2102
  initPropertySearching();
2112
2103
 
@@ -2188,16 +2179,17 @@
2188
2179
  }
2189
2180
  }
2190
2181
 
2191
- // Simplified sorting function for Fails and Errors columns
2182
+ // Simplified sorting function for Fails and Errors columns (Property Checking Statistics only)
2192
2183
  function initSorting() {
2193
- const sortIcons = document.querySelectorAll('.sort-icon');
2194
-
2184
+ // Only select sort icons that are NOT activities sort icons
2185
+ const sortIcons = document.querySelectorAll('.sort-icon:not(.activities-sort-icon)');
2186
+
2195
2187
  sortIcons.forEach(function(icon) {
2196
2188
  icon.addEventListener('click', function() {
2197
2189
  const column = this.dataset.column;
2198
2190
  const currentOrder = this.dataset.order;
2199
-
2200
- // Reset all other sort icons
2191
+
2192
+ // Reset all other property sort icons (not activities)
2201
2193
  sortIcons.forEach(function(otherIcon) {
2202
2194
  if (otherIcon !== icon) {
2203
2195
  otherIcon.dataset.order = 'none';
@@ -2205,7 +2197,7 @@
2205
2197
  otherIcon.style.color = '#6c757d';
2206
2198
  }
2207
2199
  });
2208
-
2200
+
2209
2201
  // Toggle current sort order
2210
2202
  let newOrder;
2211
2203
  if (currentOrder === 'none' || currentOrder === 'desc') {
@@ -2217,7 +2209,7 @@
2217
2209
  this.className = 'bi bi-arrow-down sort-icon active desc';
2218
2210
  this.style.color = '#dc3545';
2219
2211
  }
2220
-
2212
+
2221
2213
  this.dataset.order = newOrder;
2222
2214
  sortTable(column, newOrder);
2223
2215
  });
@@ -2922,12 +2914,11 @@
2922
2914
  // Property statistics searching function
2923
2915
  function initPropertySearching() {
2924
2916
  const searchInput = document.getElementById('property-stats-search');
2925
- const searchButton = document.querySelector('.search-btn[data-target="property-stats-search"]');
2926
- const clearButton = document.querySelector('[data-target="property-stats-search"].search-clear-btn');
2927
-
2917
+ const searchButton = document.getElementById('property-search-btn');
2918
+
2928
2919
  if (!searchInput) return;
2929
-
2930
- // Search on Enter key
2920
+
2921
+ // Search on Enter key and clear on Escape
2931
2922
  searchInput.addEventListener('keydown', function(e) {
2932
2923
  if (e.key === 'Enter') {
2933
2924
  e.preventDefault();
@@ -2936,7 +2927,7 @@
2936
2927
  clearPropertySearch(this);
2937
2928
  }
2938
2929
  });
2939
-
2930
+
2940
2931
  // Search button functionality
2941
2932
  if (searchButton) {
2942
2933
  searchButton.addEventListener('click', function() {
@@ -2944,13 +2935,6 @@
2944
2935
  });
2945
2936
  }
2946
2937
 
2947
- // Clear button functionality
2948
- if (clearButton) {
2949
- clearButton.addEventListener('click', function() {
2950
- clearPropertySearch(searchInput);
2951
- });
2952
- }
2953
-
2954
2938
  function performPropertySearch(searchInput) {
2955
2939
  const searchTerm = searchInput.value.toLowerCase().trim();
2956
2940
  const container = document.getElementById('property-stats-container');
@@ -3164,6 +3148,360 @@
3164
3148
  const currentItemsPerPage = pageSizeSelect ? parseInt(pageSizeSelect.value) : 10;
3165
3149
  goToPage(1, containerId, itemClass, currentItemsPerPage, paginationId);
3166
3150
  }
3151
+
3152
+ // Activities table searching function
3153
+ function initActivitiesSearching() {
3154
+ const searchInput = document.getElementById('activities-search');
3155
+ const searchButton = document.getElementById('activities-search-btn');
3156
+
3157
+ if (!searchInput) return;
3158
+
3159
+ // Search on Enter key and clear on Escape
3160
+ searchInput.addEventListener('keydown', function(e) {
3161
+ if (e.key === 'Enter') {
3162
+ e.preventDefault();
3163
+ performActivitiesSearch(this);
3164
+ } else if (e.key === 'Escape') {
3165
+ clearActivitiesSearch(this);
3166
+ }
3167
+ });
3168
+
3169
+ // Search button functionality
3170
+ if (searchButton) {
3171
+ searchButton.addEventListener('click', function() {
3172
+ performActivitiesSearch(searchInput);
3173
+ });
3174
+ }
3175
+
3176
+ function performActivitiesSearch(searchInput) {
3177
+ const searchTerm = searchInput.value.toLowerCase().trim();
3178
+ const container = document.getElementById('activities-container');
3179
+ const rows = container.querySelectorAll('.activity-row');
3180
+ const resultsCount = document.getElementById('activities-search-results');
3181
+
3182
+ let visibleCount = 0;
3183
+
3184
+ rows.forEach(function(row) {
3185
+ const activityName = row.querySelector('.activity-name').textContent.toLowerCase();
3186
+
3187
+ // If no search term, show all rows
3188
+ if (!searchTerm) {
3189
+ row.removeAttribute('data-search-visible');
3190
+ visibleCount++;
3191
+ } else {
3192
+ const matches = activityName.includes(searchTerm);
3193
+ if (matches) {
3194
+ row.setAttribute('data-search-visible', 'true');
3195
+ visibleCount++;
3196
+ } else {
3197
+ row.setAttribute('data-search-visible', 'false');
3198
+ }
3199
+ }
3200
+ });
3201
+
3202
+ console.log('Search performed:', {searchTerm, visibleCount, totalRows: rows.length});
3203
+
3204
+ // Update results count
3205
+ if (resultsCount) {
3206
+ if (searchTerm) {
3207
+ resultsCount.textContent = `Found ${visibleCount} of ${rows.length} activities`;
3208
+ } else {
3209
+ resultsCount.textContent = '';
3210
+ }
3211
+ }
3212
+
3213
+ // Immediately show/hide rows based on search before pagination update
3214
+ rows.forEach(function(row) {
3215
+ const searchVisible = row.getAttribute('data-search-visible');
3216
+ if (searchVisible === null || searchVisible === 'true') {
3217
+ row.style.display = '';
3218
+ } else {
3219
+ row.style.display = 'none';
3220
+ }
3221
+ });
3222
+
3223
+ console.log('Rows visibility updated, calling pagination update...');
3224
+
3225
+ // Update pagination after search
3226
+ updateActivitiesPagination();
3227
+ }
3228
+
3229
+ function clearActivitiesSearch(searchInput) {
3230
+ searchInput.value = '';
3231
+ performActivitiesSearch(searchInput);
3232
+ searchInput.focus();
3233
+ }
3234
+ }
3235
+
3236
+ // Initialize Activities page size selector
3237
+ function initActivitiesPageSize() {
3238
+ const pageSizeSelect = document.getElementById('activities-page-size');
3239
+
3240
+ if (!pageSizeSelect) return;
3241
+
3242
+ // Remove existing event listener to prevent duplicate bindings
3243
+ if (!pageSizeSelect.hasAttribute('data-activities-listener-bound')) {
3244
+ pageSizeSelect.addEventListener('change', function() {
3245
+ console.log('Activities page size changed to:', this.value);
3246
+ updateActivitiesPagination();
3247
+ });
3248
+ // Mark as having listener bound
3249
+ pageSizeSelect.setAttribute('data-activities-listener-bound', 'true');
3250
+ }
3251
+ }
3252
+
3253
+ // Activities table sorting function
3254
+ function initActivitiesSorting() {
3255
+ const sortIcon = document.getElementById('visit-count-sort');
3256
+
3257
+ if (!sortIcon) return;
3258
+
3259
+ sortIcon.addEventListener('click', function(e) {
3260
+ e.preventDefault();
3261
+ e.stopPropagation();
3262
+
3263
+ console.log('Sort icon clicked, current order:', this.dataset.order);
3264
+
3265
+ const currentOrder = this.dataset.order || 'none';
3266
+ const container = document.getElementById('activities-container');
3267
+ const rows = Array.from(container.querySelectorAll('.activity-row'));
3268
+
3269
+ console.log('Found rows:', rows.length);
3270
+
3271
+ // Reset only activities sort icons (not property stats sort icons)
3272
+ document.querySelectorAll('.activities-sort-icon').forEach(icon => {
3273
+ if (icon !== this) {
3274
+ icon.className = 'bi bi-arrow-down-up text-muted sort-icon activities-sort-icon';
3275
+ icon.dataset.order = 'none';
3276
+ }
3277
+ });
3278
+
3279
+ let newOrder;
3280
+ if (currentOrder === 'none' || currentOrder === 'desc') {
3281
+ newOrder = 'asc';
3282
+ this.className = 'bi bi-arrow-up text-primary sort-icon activities-sort-icon';
3283
+ } else {
3284
+ newOrder = 'desc';
3285
+ this.className = 'bi bi-arrow-down text-primary sort-icon activities-sort-icon';
3286
+ }
3287
+
3288
+ this.dataset.order = newOrder;
3289
+ console.log('New order set to:', newOrder);
3290
+
3291
+ // Sort rows
3292
+ rows.sort((a, b) => {
3293
+ const aCount = parseInt(a.dataset.visitCount) || 0;
3294
+ const bCount = parseInt(b.dataset.visitCount) || 0;
3295
+
3296
+ if (newOrder === 'asc') {
3297
+ return aCount - bCount;
3298
+ } else {
3299
+ return bCount - aCount;
3300
+ }
3301
+ });
3302
+
3303
+ // Clear container and re-append sorted rows
3304
+ container.innerHTML = '';
3305
+ rows.forEach(row => container.appendChild(row));
3306
+
3307
+ console.log('Sorting completed');
3308
+
3309
+ // Properly update pagination after sorting
3310
+ updateActivitiesPagination();
3311
+ });
3312
+ }
3313
+
3314
+ // Function to update Activities pagination after sorting
3315
+ function updateActivitiesPagination() {
3316
+ const container = document.getElementById('activities-container');
3317
+ const pageSizeSelect = document.getElementById('activities-page-size');
3318
+ const paginationElement = document.getElementById('activities-pagination');
3319
+
3320
+ if (!container || !paginationElement) return;
3321
+
3322
+ const allRows = Array.from(container.querySelectorAll('.activity-row'));
3323
+ const itemsPerPage = pageSizeSelect ? parseInt(pageSizeSelect.value) : 10;
3324
+
3325
+ // Filter visible rows (considering search)
3326
+ const visibleRows = allRows.filter(row => {
3327
+ const searchVisible = row.getAttribute('data-search-visible');
3328
+ return searchVisible === null || searchVisible === 'true';
3329
+ });
3330
+
3331
+ const totalItems = visibleRows.length;
3332
+ const totalPages = Math.max(1, Math.ceil(totalItems / itemsPerPage));
3333
+
3334
+ console.log('Updating pagination:', {totalItems, itemsPerPage, totalPages});
3335
+
3336
+ // Clear pagination
3337
+ paginationElement.innerHTML = '';
3338
+
3339
+ // Find pagination container
3340
+ const paginationContainer = paginationElement.closest('.d-flex');
3341
+
3342
+ // Don't show pagination if there's only one page or no items
3343
+ if (totalPages <= 1) {
3344
+ if (paginationContainer) {
3345
+ paginationContainer.style.display = 'none';
3346
+ }
3347
+ // Show visible rows and hide invisible rows
3348
+ allRows.forEach(row => {
3349
+ const searchVisible = row.getAttribute('data-search-visible');
3350
+ if (searchVisible === null || searchVisible === 'true') {
3351
+ row.style.display = '';
3352
+ } else {
3353
+ row.style.display = 'none';
3354
+ }
3355
+ });
3356
+ return;
3357
+ }
3358
+
3359
+ // Show the pagination container when needed
3360
+ if (paginationContainer) {
3361
+ paginationContainer.style.display = '';
3362
+ }
3363
+
3364
+ // Create Previous button
3365
+ const prevLi = document.createElement('li');
3366
+ prevLi.className = 'page-item';
3367
+ prevLi.id = 'activities-prev-page';
3368
+
3369
+ const prevA = document.createElement('a');
3370
+ prevA.className = 'page-link';
3371
+ prevA.href = '#';
3372
+ prevA.innerHTML = '&laquo;';
3373
+ prevA.addEventListener('click', function(e) {
3374
+ e.preventDefault();
3375
+ const currentPage = getCurrentActivitiesPage();
3376
+ if (currentPage > 1) {
3377
+ goToActivitiesPage(currentPage - 1, totalPages, itemsPerPage);
3378
+ }
3379
+ });
3380
+
3381
+ prevLi.appendChild(prevA);
3382
+ paginationElement.appendChild(prevLi);
3383
+
3384
+ // Create page number buttons
3385
+ for (let i = 1; i <= totalPages; i++) {
3386
+ const li = document.createElement('li');
3387
+ li.className = `page-item ${i === 1 ? 'active' : ''}`;
3388
+ li.setAttribute('data-page', i);
3389
+
3390
+ const a = document.createElement('a');
3391
+ a.className = 'page-link';
3392
+ a.href = '#';
3393
+ a.textContent = i;
3394
+ a.addEventListener('click', function(e) {
3395
+ e.preventDefault();
3396
+ goToActivitiesPage(i, totalPages, itemsPerPage);
3397
+ });
3398
+
3399
+ li.appendChild(a);
3400
+ paginationElement.appendChild(li);
3401
+ }
3402
+
3403
+ // Create Next button
3404
+ const nextLi = document.createElement('li');
3405
+ nextLi.className = 'page-item';
3406
+ nextLi.id = 'activities-next-page';
3407
+
3408
+ const nextA = document.createElement('a');
3409
+ nextA.className = 'page-link';
3410
+ nextA.href = '#';
3411
+ nextA.innerHTML = '&raquo;';
3412
+ nextA.addEventListener('click', function(e) {
3413
+ e.preventDefault();
3414
+ const currentPage = getCurrentActivitiesPage();
3415
+ if (currentPage < totalPages) {
3416
+ goToActivitiesPage(currentPage + 1, totalPages, itemsPerPage);
3417
+ }
3418
+ });
3419
+
3420
+ nextLi.appendChild(nextA);
3421
+ paginationElement.appendChild(nextLi);
3422
+
3423
+ // Show first page and update navigation buttons
3424
+ goToActivitiesPage(1, totalPages, itemsPerPage);
3425
+ }
3426
+
3427
+ // Function to navigate to specific page in Activities table
3428
+ function goToActivitiesPage(pageNumber, totalPages, itemsPerPage) {
3429
+ const container = document.getElementById('activities-container');
3430
+ const paginationElement = document.getElementById('activities-pagination');
3431
+
3432
+ if (!container) return;
3433
+
3434
+ const allRows = Array.from(container.querySelectorAll('.activity-row'));
3435
+
3436
+ // Filter visible rows (considering search)
3437
+ const visibleRows = allRows.filter(row => {
3438
+ const searchVisible = row.getAttribute('data-search-visible');
3439
+ return searchVisible === null || searchVisible === 'true';
3440
+ });
3441
+
3442
+ const startIndex = (pageNumber - 1) * itemsPerPage;
3443
+ const endIndex = startIndex + itemsPerPage;
3444
+
3445
+ console.log('Going to page:', {pageNumber, startIndex, endIndex, totalVisible: visibleRows.length});
3446
+
3447
+ // Hide all rows first
3448
+ allRows.forEach(row => {
3449
+ row.style.display = 'none';
3450
+ });
3451
+
3452
+ // Show rows for current page
3453
+ visibleRows.forEach((row, index) => {
3454
+ if (index >= startIndex && index < endIndex) {
3455
+ row.style.display = '';
3456
+ }
3457
+ });
3458
+
3459
+ // Update pagination active state
3460
+ if (paginationElement) {
3461
+ // Update page number buttons
3462
+ const pageItems = paginationElement.querySelectorAll('.page-item[data-page]');
3463
+ pageItems.forEach((item) => {
3464
+ const page = parseInt(item.getAttribute('data-page'));
3465
+ if (page === pageNumber) {
3466
+ item.classList.add('active');
3467
+ } else {
3468
+ item.classList.remove('active');
3469
+ }
3470
+ });
3471
+
3472
+ // Update Previous button state
3473
+ const prevButton = paginationElement.querySelector('#activities-prev-page');
3474
+ if (prevButton) {
3475
+ if (pageNumber <= 1) {
3476
+ prevButton.classList.add('disabled');
3477
+ } else {
3478
+ prevButton.classList.remove('disabled');
3479
+ }
3480
+ }
3481
+
3482
+ // Update Next button state
3483
+ const nextButton = paginationElement.querySelector('#activities-next-page');
3484
+ if (nextButton) {
3485
+ if (pageNumber >= totalPages) {
3486
+ nextButton.classList.add('disabled');
3487
+ } else {
3488
+ nextButton.classList.remove('disabled');
3489
+ }
3490
+ }
3491
+ }
3492
+ }
3493
+
3494
+ // Function to get current active page in Activities pagination
3495
+ function getCurrentActivitiesPage() {
3496
+ const paginationElement = document.getElementById('activities-pagination');
3497
+ if (!paginationElement) return 1;
3498
+
3499
+ const activeItem = paginationElement.querySelector('.page-item.active[data-page]');
3500
+ if (activeItem) {
3501
+ return parseInt(activeItem.getAttribute('data-page'));
3502
+ }
3503
+ return 1;
3504
+ }
3167
3505
  </script>
3168
3506
  </body>
3169
3507
  </html>