feat: comprehensive SEO optimization and GDPR compliance

- Added Terms of Service and Privacy Policy pages with contact info
- Implemented Google Analytics with Consent Mode v2 for GDPR compliance
- Created sitemap.xml and robots.txt for search engine optimization
- Added dynamic meta tags, Open Graph, and structured data (JSON-LD)
- Implemented GDPR consent banner with TCF 2.2 compatibility
- Enhanced sidebar with category-colored hover states and proper active/inactive styling
- Fixed all ESLint warnings for clean deployment
- Added comprehensive SEO utilities and privacy-first analytics tracking

Ready for production deployment with full legal compliance and SEO optimization.
This commit is contained in:
dwindown
2025-09-24 00:12:28 +07:00
parent dd03a7213f
commit 2e67a2bca2
19 changed files with 2327 additions and 287 deletions

View File

@@ -1276,13 +1276,45 @@ const TableEditor = () => {
})),
);
// Auto-scroll to the right to show the new column
// Auto-trigger header editing for the new column
setEditingHeader(newColumnId);
// Auto-scroll to the right to show the new column and focus on header input
setTimeout(() => {
const tableContainer = document.querySelector(".overflow-auto");
if (tableContainer) {
tableContainer.scrollLeft = tableContainer.scrollWidth;
// Try multiple selectors to find the table container
const selectors = [
'[class*="overflow-auto"][class*="max-h-"]',
'.overflow-auto',
'div[class*="overflow-auto"]'
];
let tableContainer = null;
for (const selector of selectors) {
tableContainer = document.querySelector(selector);
if (tableContainer) break;
}
}, 100);
if (tableContainer) {
// Check if horizontal scrolling is needed
const needsScroll = tableContainer.scrollWidth > tableContainer.clientWidth;
if (needsScroll) {
// Smooth scroll to the far right to show the new column
tableContainer.scrollTo({
left: tableContainer.scrollWidth - tableContainer.clientWidth,
behavior: 'smooth'
});
}
}
// Focus on the header input field after scroll
setTimeout(() => {
const headerInput = document.querySelector(`input[value="${newColumn.name}"]`);
if (headerInput) {
headerInput.focus();
headerInput.select(); // Select all text for easy replacement
}
}, 100);
}, 200);
};
// Delete selected rows
@@ -2419,7 +2451,7 @@ const TableEditor = () => {
{editingCell?.rowId === row.id &&
editingCell?.columnId === column.id ? (
(() => {
const cellValue = row[column.id] || "";
const cellValue = String(row[column.id] || "");
const isLongValue =
cellValue.length > 100 ||
cellValue.includes("\n");
@@ -2474,7 +2506,7 @@ const TableEditor = () => {
const format = detectCellFormat(
row[column.id],
);
const cellValue = row[column.id] || "";
const cellValue = String(row[column.id] || "");
const isLongValue = cellValue.length > 50;
if (format) {
@@ -2540,20 +2572,25 @@ const TableEditor = () => {
))}
{/* System Row - Add Row */}
<tr className="border-t-2 border-dashed border-gray-300 dark:border-gray-600 hover:bg-blue-50 dark:hover:bg-blue-900/20">
<td
colSpan={columns.length + 2}
className="text-center py-4"
>
<tr className="border-t-2 border-dashed border-gray-300 dark:border-gray-600">
{/* Sticky Add Row button on the left */}
<td className="sticky left-0 bg-white dark:bg-gray-800 z-20 py-4 px-4">
<button
onClick={addRow}
className="flex items-center justify-center gap-2 text-gray-500 hover:text-blue-600 px-4 py-2 rounded-lg transition-colors group"
className="flex items-center justify-center gap-2 text-gray-500 hover:text-blue-600 px-3 py-2 rounded-lg transition-colors group whitespace-nowrap"
title="Add new row"
>
<Plus className="h-4 w-4 group-hover:scale-110 transition-transform" />
<span className="text-sm font-medium">Add Row</span>
</button>
</td>
{/* Empty cells to fill the rest of the row */}
<td
colSpan={columns.length + 1}
className="py-4"
>
{/* Empty space for visual consistency */}
</td>
</tr>
</tbody>
</table>