tt-help-cli-ycl 1.4.13 → 1.4.15

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.
@@ -50,6 +50,7 @@ import {
50
50
  getJobRow,
51
51
  getJobBaseRow,
52
52
  getJob,
53
+ getBulkAuthorLocations,
53
54
  getAllJobs,
54
55
  getVideoRow,
55
56
  getAllVideoRows,
@@ -96,6 +97,9 @@ import {
96
97
  rawQuery,
97
98
  normalizeTags,
98
99
  clearTags,
100
+ getProductiveRefreshTask,
101
+ claimRefreshTag,
102
+ reportRefreshResult,
99
103
  } from "./db-tags.js";
100
104
 
101
105
  // LLM 打分
@@ -391,6 +395,7 @@ function getTargetUsersByCountryFromDb(targetLocations = [], options = {}) {
391
395
  refresh_time,
392
396
  top_video_play_count,
393
397
  top_video_href,
398
+ top_video_is_ad,
394
399
  status,
395
400
  sources
396
401
  FROM jobs
@@ -490,6 +495,54 @@ function getTargetUsersByCountryFromDb(targetLocations = [], options = {}) {
490
495
  };
491
496
  }
492
497
 
498
+ /**
499
+ * 查询已修改(modified_at IS NOT NULL)且 location_created 不在 targetLocations 中的目标商家,
500
+ * 按国家分组返回。
501
+ */
502
+ function getModifiedTargetUsersOutsideLocations(targetLocations = []) {
503
+ if (!getDb()) return { countries: [], total: 0 };
504
+
505
+ const placeholders = targetLocations.map(() => "?").join(", ");
506
+ const rows = getDb()
507
+ .prepare(
508
+ `
509
+ SELECT unique_id, nickname, follower_count, video_count,
510
+ location_created, confirmed_location, modified_at, latest_video_time, refresh_time
511
+ FROM jobs
512
+ WHERE tt_seller = 1
513
+ AND verified = 0
514
+ AND modified_at IS NOT NULL
515
+ AND location_created IS NOT NULL
516
+ AND location_created NOT IN (${placeholders})
517
+ ORDER BY location_created ASC, COALESCE(latest_video_time, 0) DESC
518
+ `,
519
+ )
520
+ .all(...targetLocations)
521
+ .map(mapJobRow);
522
+
523
+ const countryMap = new Map();
524
+ for (const row of rows) {
525
+ const country = row.locationCreated || "未知";
526
+ if (!countryMap.has(country)) {
527
+ countryMap.set(country, { users: [], userCount: 0 });
528
+ }
529
+ const entry = countryMap.get(country);
530
+ entry.users.push(row);
531
+ entry.userCount++;
532
+ }
533
+
534
+ const countries = [];
535
+ for (const [country, entry] of countryMap) {
536
+ countries.push({
537
+ country,
538
+ count: entry.userCount,
539
+ users: entry.users,
540
+ });
541
+ }
542
+
543
+ return { total: rows.length, countries };
544
+ }
545
+
493
546
  export function createStore(filePath, options = {}) {
494
547
  if (!filePath) {
495
548
  throw new Error("createStore requires an explicit .db path");
@@ -2292,6 +2345,7 @@ export function createStore(filePath, options = {}) {
2292
2345
  if (result.topRecentVideo && typeof result.topRecentVideo === "object") {
2293
2346
  user.topVideoPlayCount = result.topRecentVideo.playCount || null;
2294
2347
  user.topVideoHref = result.topRecentVideo.href || null;
2348
+ user.topVideoIsAd = result.topRecentVideo.isAd ?? null;
2295
2349
  }
2296
2350
  user.sources = [...new Set([...(user.sources || []), "processed"])];
2297
2351
  }
@@ -2518,6 +2572,7 @@ export function createStore(filePath, options = {}) {
2518
2572
  if (result.topRecentVideo && typeof result.topRecentVideo === "object") {
2519
2573
  user.topVideoPlayCount = result.topRecentVideo.playCount || null;
2520
2574
  user.topVideoHref = result.topRecentVideo.href || null;
2575
+ user.topVideoIsAd = result.topRecentVideo.isAd ?? null;
2521
2576
  }
2522
2577
  const newUsers = processDiscoveredUsers(result);
2523
2578
  const ret = updateJobInfo(uniqueId, user, false);
@@ -2543,6 +2598,7 @@ export function createStore(filePath, options = {}) {
2543
2598
  if (result.topRecentVideo && typeof result.topRecentVideo === "object") {
2544
2599
  user.topVideoPlayCount = result.topRecentVideo.playCount || null;
2545
2600
  user.topVideoHref = result.topRecentVideo.href || null;
2601
+ user.topVideoIsAd = result.topRecentVideo.isAd ?? null;
2546
2602
  }
2547
2603
  const newUsers = processDiscoveredUsers(result);
2548
2604
  return { saved: true, newUsers };
@@ -3241,6 +3297,7 @@ export function createStore(filePath, options = {}) {
3241
3297
  getProcessedUsers,
3242
3298
  getAllUsers,
3243
3299
  getUserDbCount,
3300
+ getBulkAuthorLocations,
3244
3301
  getJobsCount,
3245
3302
  getRawJobsCount,
3246
3303
  getPendingJobsCount,
@@ -3260,6 +3317,8 @@ export function createStore(filePath, options = {}) {
3260
3317
  getRawJobsPage: getRawJobsPageFromDb,
3261
3318
  getTargetUsers: getTargetUsersFromDb,
3262
3319
  getTargetUsersByCountry: getTargetUsersByCountryFromDb,
3320
+ getModifiedTargetUsersOutsideLocations: (loc) =>
3321
+ getModifiedTargetUsersOutsideLocations(loc),
3263
3322
  getStats,
3264
3323
  getStatusGroups,
3265
3324
  markGroupsDirty,
@@ -3310,6 +3369,9 @@ export function createStore(filePath, options = {}) {
3310
3369
  getTagStats,
3311
3370
  normalizeTags,
3312
3371
  clearTags,
3372
+ getProductiveRefreshTask,
3373
+ claimRefreshTag,
3374
+ reportRefreshResult,
3313
3375
  data,
3314
3376
  };
3315
3377
 
@@ -48,6 +48,7 @@ export const JOB_EXT_COLUMNS = [
48
48
  "user_create_time",
49
49
  "top_video_play_count",
50
50
  "top_video_href",
51
+ "top_video_is_ad",
51
52
  ];
52
53
 
53
54
  // 完整的 jobs 列 = 核心列 + 扩展列
@@ -88,6 +89,7 @@ export const WRITABLE_JOB_COLUMNS = new Set([
88
89
  "latest_video_time",
89
90
  "top_video_play_count",
90
91
  "top_video_href",
92
+ "top_video_is_ad",
91
93
  "user_create_time",
92
94
  ]);
93
95
 
@@ -327,6 +327,34 @@ export function getJob(uniqueId) {
327
327
  return mapJobRow(getJobRow(uniqueId));
328
328
  }
329
329
 
330
+ /**
331
+ * 批量查询作者的国家信息
332
+ * 从 jobs 和 jobs_base 表中查找已知的 location_created
333
+ * @param {string[]} uniqueIds
334
+ * @returns {Record<string, string>} { uniqueId: locationCreated }
335
+ */
336
+ export function getBulkAuthorLocations(uniqueIds) {
337
+ const db = getDb();
338
+ if (!db || uniqueIds.length === 0) return {};
339
+ // SQLite 的 IN 子句不支持数组参数,动态构建占位符
340
+ const placeholders = uniqueIds.map(() => "?").join(",");
341
+ const sql = `
342
+ SELECT unique_id, location_created FROM jobs
343
+ WHERE unique_id IN (${placeholders}) AND location_created IS NOT NULL AND location_created != ''
344
+ UNION
345
+ SELECT unique_id, location_created FROM jobs_base
346
+ WHERE unique_id IN (${placeholders}) AND location_created IS NOT NULL AND location_created != ''
347
+ `;
348
+ const rows = db.prepare(sql).all(...uniqueIds, ...uniqueIds);
349
+ const result = {};
350
+ for (const r of rows) {
351
+ if (!result[r.unique_id]) {
352
+ result[r.unique_id] = r.location_created;
353
+ }
354
+ }
355
+ return result;
356
+ }
357
+
330
358
  export function getAllJobs() {
331
359
  const db = getDb();
332
360
  if (!db) return [];
@@ -127,6 +127,7 @@ export function initDb(filePath) {
127
127
  bio_link: "TEXT",
128
128
  top_video_play_count: "INTEGER",
129
129
  top_video_href: "TEXT",
130
+ top_video_is_ad: "INTEGER",
130
131
  user_create_time: "INTEGER",
131
132
  });
132
133
 
@@ -280,6 +281,8 @@ export function initDb(filePath) {
280
281
  strategy: "TEXT",
281
282
  dead_reason: "TEXT",
282
283
  direction_id: "INTEGER",
284
+ refreshing: "INTEGER DEFAULT 0",
285
+ refreshed_at: "TEXT",
283
286
  });
284
287
 
285
288
  // ===== tag_directions 表(分层探索:通用词/分类/产品) =====
@@ -1234,3 +1234,98 @@ function getLang(country) {
1234
1234
  };
1235
1235
  return COUNTRY_LANG[country] || "en";
1236
1236
  }
1237
+
1238
+ // ===== Tag 刷新 CRUD(productive 标签轮询重新抓取) =====
1239
+
1240
+ /**
1241
+ * 获取一个待刷新的 productive 标签(score > 70 且未被刷新锁定)
1242
+ * 按 refreshed_at ASC 排序(最久未刷新的优先),NULL 排最前
1243
+ */
1244
+ export function getProductiveRefreshTask() {
1245
+ const db = getDb();
1246
+ if (!db) return null;
1247
+
1248
+ // 先清理超时的 refreshing 锁(超过 30 分钟未释放)
1249
+ db.prepare(
1250
+ `UPDATE tags SET refreshing = 0
1251
+ WHERE refreshing = 1 AND refreshed_at IS NOT NULL
1252
+ AND refreshed_at < datetime('now', '-30 minutes')`,
1253
+ ).run();
1254
+
1255
+ const row = db
1256
+ .prepare(
1257
+ `SELECT * FROM tags
1258
+ WHERE status = 'productive' AND score > 70 AND refreshing = 0
1259
+ ORDER BY refreshed_at IS NULL DESC, refreshed_at ASC
1260
+ LIMIT 1`,
1261
+ )
1262
+ .get();
1263
+ return row ? parseTagRow(row) : null;
1264
+ }
1265
+
1266
+ /**
1267
+ * 锁定一个 productive 标签用于刷新(防并发冲突)
1268
+ */
1269
+ export function claimRefreshTag(tag) {
1270
+ const db = getDb();
1271
+ if (!db) return { ok: false, error: "db not ready" };
1272
+
1273
+ const normalized = tag.replace(/^#+/, "").trim().toLowerCase();
1274
+ if (!normalized || normalized.length < 2) {
1275
+ return { ok: false, error: "invalid tag" };
1276
+ }
1277
+
1278
+ // 同时尝试匹配带 # 和不带 # 的格式(兼容旧数据)
1279
+ const result = db
1280
+ .prepare(
1281
+ `UPDATE tags SET refreshing = 1, refreshed_at = datetime('now')
1282
+ WHERE (tag = ? OR tag = ?) AND status = 'productive' AND score > 70 AND refreshing = 0`,
1283
+ )
1284
+ .run(normalized, `#${normalized}`);
1285
+ if (result.changes === 0) {
1286
+ const row = db
1287
+ .prepare("SELECT status, refreshing FROM tags WHERE tag = ? OR tag = ?")
1288
+ .get(normalized, `#${normalized}`);
1289
+ if (!row) return { ok: false, error: "tag not found" };
1290
+ if (row.refreshing) return { ok: false, error: "already being refreshed" };
1291
+ return {
1292
+ ok: false,
1293
+ error: `tag status is ${row.status}, not eligible for refresh`,
1294
+ };
1295
+ }
1296
+ return { ok: true, tag: normalized };
1297
+ }
1298
+
1299
+ /**
1300
+ * 报告刷新结果并解锁标签(不改变 score/status,只更新 refreshed_at 和 pushed_users)
1301
+ */
1302
+ export function reportRefreshResult(tag, fields) {
1303
+ const db = getDb();
1304
+ if (!db) return { ok: false, error: "db not ready" };
1305
+ const { pushedUsers, error } = fields;
1306
+ const now = new Date().toISOString();
1307
+ const normalized = tag.replace(/^#+/, "").trim().toLowerCase();
1308
+
1309
+ try {
1310
+ const result = db
1311
+ .prepare(
1312
+ `UPDATE tags SET
1313
+ refreshing = 0,
1314
+ refreshed_at = ?,
1315
+ pushed_users = COALESCE(pushed_users, 0) + COALESCE(?, 0),
1316
+ last_error = COALESCE(?, last_error),
1317
+ score_count = score_count + 1
1318
+ WHERE tag = ? OR tag = ?`,
1319
+ )
1320
+ .run(
1321
+ now,
1322
+ pushedUsers ?? null,
1323
+ error ?? null,
1324
+ normalized,
1325
+ `#${normalized}`,
1326
+ );
1327
+ return { ok: result.changes > 0, tag: normalized };
1328
+ } catch (e) {
1329
+ return { ok: false, error: e.message };
1330
+ }
1331
+ }
@@ -735,6 +735,9 @@ async function selectLocation(uniqueId, location) {
735
735
  userInMem.locationCreated = location;
736
736
  userInMem.modifiedAt = Date.now();
737
737
  }
738
+
739
+ // 刷新"其他国家"区域(修改后可能需要更新统计)
740
+ fetchTargetOtherCountries();
738
741
  } catch (e) {
739
742
  showToast("更新失败: " + e.message, true);
740
743
  } finally {
@@ -1023,6 +1026,30 @@ document.getElementById("statTargetCard").addEventListener("click", () => {
1023
1026
  window.addEventListener("hashchange", handleRoute);
1024
1027
  window.addEventListener("DOMContentLoaded", handleRoute);
1025
1028
 
1029
+ // 回顶部按钮:仅在 targetPage 激活时监听滚动
1030
+ window.addEventListener(
1031
+ "scroll",
1032
+ function () {
1033
+ const btn = document.getElementById("backToTop");
1034
+ if (!btn) return;
1035
+ const targetPage = document.getElementById("targetPage");
1036
+ if (!targetPage || !targetPage.classList.contains("active")) {
1037
+ btn.classList.remove("visible");
1038
+ return;
1039
+ }
1040
+ if (window.scrollY > 400) {
1041
+ btn.classList.add("visible");
1042
+ } else {
1043
+ btn.classList.remove("visible");
1044
+ }
1045
+ },
1046
+ { passive: true },
1047
+ );
1048
+
1049
+ function scrollToTop() {
1050
+ window.scrollTo({ top: 0, behavior: "smooth" });
1051
+ }
1052
+
1026
1053
  function handleRoute() {
1027
1054
  const hash = window.location.hash;
1028
1055
  if (hash === "#pending") {
@@ -1410,18 +1437,20 @@ function renderTagsTable() {
1410
1437
  const created = t.created_at || "-";
1411
1438
  const score = t.score != null ? t.score.toFixed(1) : "-";
1412
1439
  const authorCount = t.author_count ?? "-";
1413
- const totalPosts = t.total_posts ?? "-";
1414
1440
  const matchedAuthors = t.matched_authors ?? "-";
1415
1441
  const pushedUsers = t.pushed_users ?? "-";
1442
+ const refreshedAt = t.refreshed_at
1443
+ ? t.refreshed_at.slice(0, 16).replace("T", " ")
1444
+ : "-";
1416
1445
 
1417
1446
  return `<tr>
1418
1447
  <td style="color:#9ca3af;font-size:12px;text-align:center">${i + 1}</td>
1419
1448
  <td style="font-weight:600;color:#e0e0e0">#${escapeHtml(t.tag)}</td>
1420
- <td style="color:${t.score >= 5 ? "#22c55e" : t.score >= 3 ? "#facc15" : "#888"}">${score}</td>
1421
- <td>${authorCount}</td>
1422
- <td>${totalPosts}</td>
1423
- <td>${matchedAuthors}</td>
1424
- <td>${pushedUsers}</td>
1449
+ <td style="text-align:center;color:${t.score >= 5 ? "#22c55e" : t.score >= 3 ? "#facc15" : "#888"}">${score}</td>
1450
+ <td style="text-align:center">${authorCount}</td>
1451
+ <td style="text-align:center;font-size:11px;color:#9ca3af">${refreshedAt}</td>
1452
+ <td style="text-align:center">${matchedAuthors}</td>
1453
+ <td style="text-align:center">${pushedUsers}</td>
1425
1454
  <td style="font-size:11px">${countries}</td>
1426
1455
  <td>${statusTag}</td>
1427
1456
  <td style="font-size:11px;color:#888">${source}</td>
@@ -1517,6 +1546,7 @@ function showTargetPage() {
1517
1546
  document.getElementById("tagsPage").classList.remove("active");
1518
1547
  showLoading("正在加载目标商家数据...");
1519
1548
  fetchTargetByCountry();
1549
+ fetchTargetOtherCountries();
1520
1550
  // 同步统计
1521
1551
  if (currentStats) {
1522
1552
  document.getElementById("targetPageStatTotal").textContent = formatStatNum(
@@ -1535,6 +1565,44 @@ let currentTargetSeq = 0;
1535
1565
  let currentTargetSort = { key: null, asc: true };
1536
1566
  const TARGET_PAGE_SIZE = 200;
1537
1567
 
1568
+ async function fetchTargetOtherCountries() {
1569
+ try {
1570
+ const res = await fetch("/api/target-users-other-countries");
1571
+ const data = await res.json();
1572
+ renderTargetOtherCountries(data.countries || []);
1573
+ } catch (e) {
1574
+ console.error("获取其他国家数据失败:", e);
1575
+ }
1576
+ }
1577
+
1578
+ function renderTargetOtherCountries(countries) {
1579
+ const section = document.getElementById("targetOtherSection");
1580
+ const grid = document.getElementById("targetOtherGrid");
1581
+ if (!section || !grid) return;
1582
+
1583
+ if (!countries.length) {
1584
+ section.style.display = "none";
1585
+ return;
1586
+ }
1587
+ section.style.display = "block";
1588
+ const total = countries.reduce((sum, c) => sum + (c.count || 0), 0);
1589
+ grid.innerHTML = countries
1590
+ .map((c) => {
1591
+ const cnt = c.count || 0;
1592
+ const pct = total > 0 ? ((cnt / total) * 100).toFixed(1) : "0.0";
1593
+ const safeCountry = escapeJsString(c.country);
1594
+ const sel = currentTargetLocation === c.country ? " selected" : "";
1595
+ return `
1596
+ <div class="pending-country-item other-country-item${sel}"
1597
+ onclick="filterTargetByCountry('${safeCountry}')">
1598
+ <div class="country-name">${c.country}</div>
1599
+ <div class="country-count">${cnt}</div>
1600
+ <div class="country-label">${pct}%</div>
1601
+ </div>`;
1602
+ })
1603
+ .join("");
1604
+ }
1605
+
1538
1606
  async function fetchTargetByCountry() {
1539
1607
  try {
1540
1608
  const res = await fetch("/api/target-users-by-country?summary=1");
@@ -1712,9 +1780,12 @@ function renderTargetTable() {
1712
1780
  u.topVideoPlayCount != null && u.topVideoPlayCount >= 1000
1713
1781
  ? formatNum(u.topVideoPlayCount)
1714
1782
  : "-";
1783
+ const adTag = u.topVideoIsAd
1784
+ ? ' <span style="color:red">(AD)</span>'
1785
+ : "";
1715
1786
  const topPlayCountCell = u.topVideoHref
1716
- ? `<td data-label="最大播放量" style="font-size:11px;color:#888"><a href="${u.topVideoHref}" target="_blank" style="color:#3b82f6;text-decoration:none" title="点击查看视频">${topPlayCount}</a></td>`
1717
- : `<td data-label="最大播放量" style="font-size:11px;color:#888">${topPlayCount}</td>`;
1787
+ ? `<td data-label="最大播放量" style="font-size:11px;color:#888"><a href="${u.topVideoHref}" target="_blank" style="color:#3b82f6;text-decoration:none" title="点击查看视频">${topPlayCount}${adTag}</a></td>`
1788
+ : `<td data-label="最大播放量" style="font-size:11px;color:#888">${topPlayCount}${adTag}</td>`;
1718
1789
 
1719
1790
  const editIcon = ' <span style="font-size:10px;opacity:0.5">✏️</span>';
1720
1791
  const locationCell = u.modifiedAt
@@ -1782,13 +1853,9 @@ function filterTargetByCountry(country) {
1782
1853
  }
1783
1854
 
1784
1855
  function clearTargetFilters() {
1785
- currentTargetLocation = "";
1856
+ // 只清空搜索关键词,保留当前选中的国家筛选
1786
1857
  const searchInput = document.getElementById("targetSearchInput");
1787
- const locationFilter = document.getElementById("targetLocationFilter");
1788
1858
  if (searchInput) searchInput.value = "";
1789
- if (locationFilter) locationFilter.value = "";
1790
- const btn = document.getElementById("targetReprocessBtn");
1791
- if (btn) btn.style.display = "none";
1792
1859
  if (currentTargetSummary) {
1793
1860
  renderTargetCountryGrid(currentTargetSummary.countries || []);
1794
1861
  loadTargetUsersPage();
@@ -328,6 +328,11 @@
328
328
  <span style="color:#666;font-size:12px">加载中...</span>
329
329
  </div>
330
330
  <div class="muted-tip">点击国家卡片可筛选列表。</div>
331
+ <div class="target-other-section" id="targetOtherSection" style="display:none">
332
+ <h3 style="color:#f59e0b;margin-top:20px;margin-bottom:12px">🌍 其他国家 (已修改)</h3>
333
+ <div class="pending-country-grid" id="targetOtherGrid"></div>
334
+ <div class="muted-tip">这些商家已被手动修改到非目标国家。</div>
335
+ </div>
331
336
  </div>
332
337
  <div class="target-table-card">
333
338
  <h3>目标商家列表</h3>
@@ -361,6 +366,7 @@
361
366
  </div>
362
367
  </div>
363
368
  </div>
369
+ <div id="backToTop" title="回到顶部" onclick="scrollToTop()">↑</div>
364
370
  </div>
365
371
  <div id="tagsPage">
366
372
  <div class="stats" style="margin-bottom:16px">
@@ -415,7 +421,7 @@
415
421
  <th style="min-width:120px">关键词</th>
416
422
  <th class="sortable-tag" data-sort="score">评分 <span class="sort-icon">↕</span></th>
417
423
  <th class="sortable-tag" data-sort="author_count">作者数 <span class="sort-icon">↕</span></th>
418
- <th class="sortable-tag" data-sort="total_posts">帖子数 <span class="sort-icon">↕</span></th>
424
+ <th class="sortable-tag" data-sort="refreshed_at">最近刷新 <span class="sort-icon">↕</span></th>
419
425
  <th class="sortable-tag" data-sort="matched_authors">匹配作者 <span class="sort-icon">↕</span></th>
420
426
  <th class="sortable-tag" data-sort="pushed_users">推送用户 <span class="sort-icon">↕</span></th>
421
427
  <th>国家</th>
@@ -839,6 +839,36 @@ td.user-id:hover {
839
839
  display: block;
840
840
  }
841
841
 
842
+ #backToTop {
843
+ position: fixed;
844
+ bottom: 32px;
845
+ right: 32px;
846
+ width: 44px;
847
+ height: 44px;
848
+ border-radius: 50%;
849
+ background: rgba(254, 44, 85, 0.85);
850
+ color: #fff;
851
+ font-size: 20px;
852
+ display: none;
853
+ align-items: center;
854
+ justify-content: center;
855
+ cursor: pointer;
856
+ z-index: 999;
857
+ box-shadow: 0 4px 16px rgba(254, 44, 85, 0.35);
858
+ transition: opacity 0.25s, transform 0.25s, background 0.2s;
859
+ user-select: none;
860
+ }
861
+
862
+ #backToTop:hover {
863
+ background: #e61944;
864
+ transform: translateY(-2px);
865
+ box-shadow: 0 6px 20px rgba(254, 44, 85, 0.5);
866
+ }
867
+
868
+ #backToTop.visible {
869
+ display: flex;
870
+ }
871
+
842
872
  #mainPage.hidden {
843
873
  display: none;
844
874
  }
@@ -1004,6 +1034,19 @@ td.user-id:hover {
1004
1034
  color: #ddd6fe;
1005
1035
  }
1006
1036
 
1037
+ .other-country-item {
1038
+ border-color: #f59e0b;
1039
+ }
1040
+
1041
+ .other-country-item .country-name {
1042
+ color: #f59e0b;
1043
+ }
1044
+
1045
+ .other-country-item:hover {
1046
+ border-color: #fbbf24;
1047
+ background: #3a3520;
1048
+ }
1049
+
1007
1050
  .back-btn {
1008
1051
  padding: 6px 14px;
1009
1052
  border: 1px solid #333;