feat: Add Gemini AI integration for product title and description optimization

- Implemented Gemini API service for generating optimized product titles and descriptions based on Google Merchant Center guidelines.
- Added settings for Gemini API key and model selection in user settings.
- Enhanced product management views to support AI-generated suggestions for titles and descriptions.
- Enabled state saving for various data tables across campaign, terms, logs, and products views.
- Introduced AI prompt templates for generating product descriptions and categories.
This commit is contained in:
2026-02-22 00:44:03 +01:00
parent 192eb11f66
commit 7573312038
17 changed files with 1259 additions and 536 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -799,6 +799,127 @@ table {
}
}
// --- Unified app tables (clients/campaigns/products/logs) ---
.clients-table-wrap,
.campaigns-table-wrap,
.products-table-wrap,
.logs-table-wrap {
background: $cWhite;
border-radius: 10px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
overflow-x: auto;
max-width: 100%;
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
.table {
margin: 0;
width: 100% !important;
thead th {
background: #F8FAFC;
border-bottom: 2px solid $cBorder;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #8899A6;
padding: 12px 16px;
white-space: nowrap;
}
tbody td {
padding: 10px 16px;
font-size: 13px;
color: $cTextDark;
vertical-align: middle;
border-bottom: 1px solid #F1F5F9;
}
tbody tr:hover td {
background: #F8FAFC;
}
}
.dt-layout-row {
padding: 14px 20px;
margin: 0 !important;
border-top: 1px solid #F1F5F9;
&:first-child {
display: none;
}
}
.dt-info {
font-size: 13px;
color: #8899A6;
}
.dt-paging {
.pagination {
margin: 0;
padding: 0;
list-style: none;
display: flex;
align-items: center;
gap: 6px;
.page-item {
.page-link {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 36px;
width: fit-content;
height: 36px;
padding: 0 14px;
border-radius: 8px;
font-size: 13px;
font-weight: 500;
border: 1px solid $cBorder;
background: $cWhite;
color: $cText;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
line-height: 1;
white-space: nowrap;
&:hover {
background: #EEF2FF;
color: $cPrimary;
border-color: $cPrimary;
}
}
&.active .page-link {
background: $cPrimary;
color: $cWhite;
border-color: $cPrimary;
font-weight: 600;
}
&.disabled .page-link {
opacity: 0.35;
cursor: default;
pointer-events: none;
}
}
}
}
.dt-processing {
background: rgba($cWhite, 0.9);
color: $cText;
font-size: 14px;
}
}
// --- Cards ---
.card {
background: $cWhite;
@@ -1094,35 +1215,7 @@ table {
}
.clients-table-wrap {
background: $cWhite;
border-radius: 10px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
overflow: hidden;
.table {
margin: 0;
thead th {
background: #F8FAFC;
border-bottom: 2px solid $cBorder;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #8899A6;
padding: 14px 20px;
}
tbody td {
padding: 14px 20px;
vertical-align: middle;
border-bottom: 1px solid #F1F5F9;
}
tbody tr:hover {
background: #F8FAFC;
}
.client-id {
color: #8899A6;
font-size: 13px;
@@ -1638,120 +1731,8 @@ table {
}
.campaigns-table-wrap {
background: $cWhite;
border-radius: 10px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
overflow-x: auto;
-ms-overflow-style: none;
scrollbar-width: none;
max-width: 100%;
&::-webkit-scrollbar {
display: none;
}
.table {
margin: 0;
width: 100% !important;
thead th {
background: #F8FAFC;
border-bottom: 2px solid $cBorder;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #8899A6;
padding: 12px 16px;
white-space: nowrap;
}
tbody td {
padding: 10px 16px;
vertical-align: middle;
border-bottom: 1px solid #F1F5F9;
font-size: 13px;
}
tbody tr:hover {
background: #F8FAFC;
}
}
// DataTables 2.x overrides
.dt-layout-row {
padding: 14px 20px;
margin: 0 !important;
border-top: 1px solid #F1F5F9;
// Ukryj wiersz z search/length jeśli pusty
&:first-child {
display: none;
}
}
.dt-info {
font-size: 13px;
color: #8899A6;
}
.dt-paging {
.pagination {
margin: 0;
padding: 0;
list-style: none;
display: flex;
align-items: center;
gap: 6px;
.page-item {
.page-link {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 36px;
width: fit-content;
height: 36px;
padding: 0 14px;
border-radius: 8px;
font-size: 13px;
font-weight: 500;
border: 1px solid $cBorder;
background: $cWhite;
color: $cText;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
line-height: 1;
white-space: nowrap;
&:hover {
background: #EEF2FF;
color: $cPrimary;
border-color: $cPrimary;
}
}
&.active .page-link {
background: $cPrimary;
color: $cWhite;
border-color: $cPrimary;
font-weight: 600;
}
&.disabled .page-link {
opacity: 0.35;
cursor: default;
pointer-events: none;
}
}
}
}
.dt-processing {
background: rgba($cWhite, 0.9);
color: $cText;
font-size: 14px;
width: 100%;
}
}
@@ -1990,37 +1971,8 @@ table {
}
.products-table-wrap {
background: $cWhite;
border-radius: 10px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
overflow: hidden;
.table {
margin: 0;
width: 100% !important;
thead th {
background: #F8FAFC;
border-bottom: 2px solid $cBorder;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.3px;
color: #8899A6;
padding: 10px 8px;
white-space: nowrap;
}
tbody td {
padding: 6px 8px;
vertical-align: middle;
border-bottom: 1px solid #F1F5F9;
font-size: 12px;
}
tbody tr:hover {
background: #F8FAFC;
}
width: 100%;
// Kompaktowe inputy w tabeli
input.min_roas,
@@ -2034,80 +1986,6 @@ table {
background: $cWhite;
}
}
// DataTables 2.x overrides (identyczne z campaigns)
.dt-layout-row {
padding: 14px 20px;
margin: 0 !important;
border-top: 1px solid #F1F5F9;
&:first-child {
display: none;
}
}
.dt-info {
font-size: 13px;
color: #8899A6;
}
.dt-paging {
.pagination {
margin: 0;
padding: 0;
list-style: none;
display: flex;
align-items: center;
gap: 6px;
.page-item {
.page-link {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 36px;
height: 36px;
padding: 0 14px;
border-radius: 8px;
font-size: 13px;
font-weight: 500;
border: 1px solid $cBorder;
background: $cWhite;
color: $cText;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
line-height: 1;
white-space: nowrap;
&:hover {
background: #EEF2FF;
color: $cPrimary;
border-color: $cPrimary;
}
}
&.active .page-link {
background: $cPrimary;
color: $cWhite;
border-color: $cPrimary;
font-weight: 600;
}
&.disabled .page-link {
opacity: 0.35;
cursor: default;
pointer-events: none;
}
}
}
}
.dt-processing {
background: rgba($cWhite, 0.9);
color: $cText;
font-size: 14px;
}
}
// Przycisk usuwania w wierszu
@@ -2274,6 +2152,18 @@ table {
border-color: #B45309;
}
}
&.btn-ai-gemini {
border-color: #4285F4;
background: linear-gradient(135deg, #E8F0FE, #D2E3FC);
color: #1A73E8;
&:hover {
background: linear-gradient(135deg, #4285F4, #1A73E8);
color: #FFF;
border-color: #1A73E8;
}
}
}
// --- Form container ---
@@ -3468,71 +3358,6 @@ table#products {
}
}
.products-page table#products>thead>tr>th {
position: sticky;
top: 0;
z-index: 2;
background-color: #111827 !important;
color: #E5E7EB !important;
border-bottom: 1px solid #0B1220 !important;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: .4px;
padding: 10px 12px;
white-space: nowrap;
}
.products-page #products thead th .dt-column-order {
display: none !important;
}
.products-page #products thead th.dt-orderable-asc,
.products-page #products thead th.dt-orderable-desc {
cursor: pointer;
padding-right: 34px;
overflow: hidden;
}
.products-page #products thead th .dt-column-title {
display: block;
overflow: hidden;
text-overflow: ellipsis;
padding-right: 2px;
}
.products-page #products thead th.dt-orderable-asc::after,
.products-page #products thead th.dt-orderable-desc::after {
content: '\2195';
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
width: 16px;
height: 16px;
border-radius: 999px;
font-size: 12px;
font-weight: 700;
line-height: 16px;
text-align: center;
color: #E5E7EB;
background: #374151;
}
.products-page #products thead th.dt-ordering-asc::after,
.products-page #products thead th[aria-sort="ascending"]::after {
content: '\25B2';
color: #FFFFFF;
background: #2563EB;
}
.products-page #products thead th.dt-ordering-desc::after,
.products-page #products thead th[aria-sort="descending"]::after {
content: '\25BC';
color: #FFFFFF;
background: #2563EB;
}
// ===========================
// LOGS PAGE
// ===========================
@@ -3609,111 +3434,8 @@ table#products {
}
.logs-table-wrap {
background: $cWhite;
border-radius: 10px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
overflow: hidden;
.table {
margin: 0;
thead th {
background: #F0F4FA;
border-bottom: 2px solid $cBorder;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #8899A6;
padding: 12px 14px;
white-space: nowrap;
}
tbody td {
padding: 10px 14px;
font-size: 13px;
color: $cTextDark;
vertical-align: middle;
border-bottom: 1px solid #EEF2F7;
}
tbody tr:hover td {
background: #F8FAFD;
}
}
.dt-layout-row {
padding: 14px 20px;
margin: 0 !important;
border-top: 1px solid #F1F5F9;
&:first-child {
display: none;
}
}
.dt-info {
font-size: 13px;
color: #8899A6;
}
.dt-paging {
.pagination {
margin: 0;
padding: 0;
list-style: none;
display: flex;
align-items: center;
gap: 6px;
.page-item {
.page-link {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 36px;
width: fit-content;
height: 36px;
padding: 0 14px;
border-radius: 8px;
font-size: 13px;
font-weight: 500;
border: 1px solid $cBorder;
background: $cWhite;
color: $cText;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
line-height: 1;
white-space: nowrap;
&:hover {
background: #EEF2FF;
color: $cPrimary;
border-color: $cPrimary;
}
}
&.active .page-link {
background: $cPrimary;
color: $cWhite;
border-color: $cPrimary;
font-weight: 600;
}
&.disabled .page-link {
opacity: 0.35;
cursor: default;
pointer-events: none;
}
}
}
}
.dt-processing {
background: rgba($cWhite, 0.9);
color: $cText;
font-size: 14px;
width: 100%;
}
}
@@ -3741,4 +3463,9 @@ table#products {
background: #FEF3C7;
color: #92400E;
}
}
.js-title-alt-apply {
color: #000;
justify-content: flex-start;
}