feat: Enhanced What's New feature with NON_TOOLS category and global footer

 What's New Feature & Navigation Improvements:
- Added attractive 'What's New' button to homepage with gradient design and sparkle effects
- Created NON_TOOLS category for better navigation organization (Home, What's New)
- Separated navigation items in sidebar and mobile menu with clear visual hierarchy
- Implemented unified global footer across all pages for consistency

🎨 Design Enhancements:
- Stunning gradient button with indigo→purple→pink colors and hover animations
- Perfect placement between stats and tools grid for maximum visibility
- Consistent indigo-purple theming for non-tools category
- Professional sparkle effects and scale transforms on hover

🔧 Technical Improvements:
- Removed duplicate footer from Terms of Service page
- Unified footer implementation reduces code duplication
- Enhanced mobile dropdown with proper NON_TOOLS separation
- Updated sidebar with category-based styling and separators

📁 Files Modified:
- /src/config/tools.js - Added NON_TOOLS category and What's New entry
- /src/components/ToolSidebar.js - Separated NON_TOOLS with visual hierarchy
- /src/components/Layout.js - Updated mobile menu and implemented global footer
- /src/pages/Home.js - Added attractive What's New button with animations
- /src/pages/TermsOfService.js - Removed duplicate footer
- /src/pages/ReleaseNotes.js - Updated with latest implementation details
This commit is contained in:
dwindown
2025-09-24 19:02:12 +07:00
parent 21d0406ece
commit 7792190ea1
8 changed files with 724 additions and 160 deletions

View File

@@ -51,6 +51,8 @@ const TableEditor = () => {
const [originalFileName, setOriginalFileName] = useState(""); // For export naming
const [isTableFullscreen, setIsTableFullscreen] = useState(false); // For fullscreen table view
const [frozenColumns, setFrozenColumns] = useState(0); // Number of columns to freeze on horizontal scroll
const [columnWidths, setColumnWidths] = useState({}); // Store custom column widths
const [resizing, setResizing] = useState(null); // Track which column is being resized
const [showClearConfirmModal, setShowClearConfirmModal] = useState(false); // For clear confirmation modal
const [showInputChangeModal, setShowInputChangeModal] = useState(false); // For input method change confirmation
const [pendingTabChange, setPendingTabChange] = useState(null); // Store pending tab change
@@ -1162,6 +1164,40 @@ const TableEditor = () => {
setData([...data, newRow]);
};
// Column resize functions
const getColumnWidth = (columnId) => {
return columnWidths[columnId] || 150; // Default width
};
const handleResizeStart = (e, columnId) => {
e.preventDefault();
const startX = e.clientX;
const startWidth = getColumnWidth(columnId);
setResizing({ columnId, startX, startWidth });
const handleMouseMove = (e) => {
if (!resizing && resizing?.columnId === columnId) return;
const deltaX = e.clientX - startX;
const newWidth = Math.max(50, startWidth + deltaX); // Minimum width of 50px
setColumnWidths(prev => ({
...prev,
[columnId]: newWidth
}));
};
const handleMouseUp = () => {
setResizing(null);
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
};
// Add new column
const addColumn = () => {
const newColumnId = `col_${Date.now()}`;
@@ -2185,11 +2221,12 @@ const TableEditor = () => {
<thead className="bg-gray-50 dark:bg-gray-700 sticky top-0 z-10">
<tr>
<th
className={`px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider ${
className={`px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider border-r border-gray-200 dark:border-gray-600 ${
frozenColumns > 0
? "sticky left-0 z-20 bg-blue-50 dark:!bg-blue-900 w-12"
: "w-12"
? "sticky left-0 z-20 bg-blue-50 dark:!bg-blue-900"
: ""
}`}
style={{ width: '40px', maxWidth: '40px', minWidth: '40px' }}
>
<input
type="checkbox"
@@ -2214,24 +2251,23 @@ const TableEditor = () => {
{columns.map((column, index) => {
const isFrozen = index < frozenColumns;
const leftOffset = isFrozen
? 45 + index * 150 // 45px for checkbox column + 150px per frozen column (no gap)
? 40 + columns.slice(0, index).reduce((acc, col) => acc + getColumnWidth(col.id), 0)
: 0;
return (
<th
key={column.id}
className={`px-4 py-3 text-left text-sm font-medium text-gray-500 dark:text-gray-300 tracking-wider hover:bg-gray-100 dark:hover:bg-gray-600 ${
className={`relative px-4 py-3 text-left text-sm font-medium text-gray-500 dark:text-gray-300 tracking-wider hover:bg-gray-100 dark:hover:bg-gray-600 border-r border-gray-200 dark:border-gray-600 ${
isFrozen
? "sticky z-20 bg-blue-50 dark:!bg-blue-900 min-w-[150px]"
: "min-w-0"
? "sticky z-20 bg-blue-50 dark:!bg-blue-900"
: ""
}`}
style={
isFrozen
? {
left: `${leftOffset}px`,
}
: {}
}
style={{
width: `${getColumnWidth(column.id)}px`,
minWidth: `${getColumnWidth(column.id)}px`,
maxWidth: `${getColumnWidth(column.id)}px`,
...(isFrozen ? { left: `${leftOffset}px` } : {})
}}
>
<div className="flex items-center gap-2">
<input
@@ -2287,6 +2323,13 @@ const TableEditor = () => {
</button>
</div>
</div>
{/* Resize handle */}
<div
className="absolute right-0 top-0 bottom-0 w-1 cursor-col-resize hover:bg-blue-500 hover:w-1.5 transition-all z-30"
onMouseDown={(e) => handleResizeStart(e, column.id)}
title="Drag to resize column"
/>
</th>
);
})}
@@ -2310,11 +2353,12 @@ const TableEditor = () => {
className="hover:bg-gray-50 dark:hover:bg-gray-700"
>
<td
className={`px-4 py-3 ${
className={`px-4 py-3 border-r border-gray-200 dark:border-gray-600 ${
frozenColumns > 0
? "sticky left-0 z-10 bg-blue-50 dark:!bg-blue-900 w-12"
: "w-12"
? "sticky left-0 z-10 bg-blue-50 dark:!bg-blue-900"
: ""
}`}
style={{ width: '40px', maxWidth: '40px', minWidth: '40px' }}
>
<input
type="checkbox"
@@ -2334,24 +2378,23 @@ const TableEditor = () => {
{columns.map((column, index) => {
const isFrozen = index < frozenColumns;
const leftOffset = isFrozen
? 45 + index * 150 // 45px for checkbox column + 150px per frozen column (no gap)
? 40 + columns.slice(0, index).reduce((acc, col) => acc + getColumnWidth(col.id), 0)
: 0;
return (
<td
key={column.id}
className={`px-4 py-3 text-sm text-gray-900 dark:text-gray-100 ${
className={`px-4 py-3 text-sm text-gray-900 dark:text-gray-100 border-r border-gray-200 dark:border-gray-600 break-words ${
isFrozen
? "sticky z-10 bg-blue-50 dark:!bg-blue-900 min-w-[150px]"
? "sticky z-10 bg-blue-50 dark:!bg-blue-900"
: ""
}`}
style={
isFrozen
? {
left: `${leftOffset}px`,
}
: {}
}
style={{
width: `${getColumnWidth(column.id)}px`,
minWidth: `${getColumnWidth(column.id)}px`,
maxWidth: `${getColumnWidth(column.id)}px`,
...(isFrozen ? { left: `${leftOffset}px` } : {})
}}
>
{editingCell?.rowId === row.id &&
editingCell?.columnId === column.id ? (
@@ -2478,24 +2521,19 @@ const TableEditor = () => {
{/* System Row - Add Row */}
<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">
<td
colSpan={columns.length + 1}
className="py-4 px-4 relative"
>
<button
onClick={addRow}
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"
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 sticky left-4"
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>