feat: mobile UX polish — FA icons, weather fix, header swap, sticky fix

- Replace emoji project icons with FontAwesome solid icons (46 icons)
  with proper icon picker grid and legacy emoji fallback
- Fix weather temperature unreadable on mobile (white on white) by
  adding explicit dark/light color
- Fix weekday sticky header on mobile: correct top offset for
  time-grid scroll container, use overflow-x: clip to not break sticky
- Swap mobile header: quick settings (PanelLeftOpen) on left,
  burger menu (settings) on right, remove search icon (available
  in left sidebar)

v1.46.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
mARTin 2026-03-17 23:57:55 +01:00
parent 183fd6ea48
commit c84fd4c60e
3 changed files with 86 additions and 45 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "my-weekly-todo-list", "name": "my-weekly-todo-list",
"version": "1.45.0", "version": "1.46.0",
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view", "description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -3879,10 +3879,14 @@ h3 {
.weekly-day-header { .weekly-day-header {
position: sticky; position: sticky;
top: calc(48px + env(safe-area-inset-top, 0px)); top: calc(48px + env(safe-area-inset-top, 0px));
z-index: 10; z-index: 40;
background: var(--weekly-bg, white); background: var(--weekly-bg, white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
} }
/* In time-grid mode, the scroll container is time-grid-wrapper, so top=0 */
.time-grid-on .weekly-day-header {
top: 0 !important;
}
} }
@media (max-width: 480px) { @media (max-width: 480px) {
@ -4308,7 +4312,7 @@ h3 {
@media (max-width: 768px) { @media (max-width: 768px) {
.time-grid-on .weekly-days-grid { .time-grid-on .weekly-days-grid {
overflow-y: auto !important; overflow-y: auto !important;
overflow-x: hidden !important; overflow-x: clip !important;
} }
.time-grid-on .weekly-day-column { .time-grid-on .weekly-day-column {
overflow-y: visible !important; overflow-y: visible !important;
@ -4358,8 +4362,12 @@ h3 {
} }
@media (max-width: 768px) { @media (max-width: 768px) {
/* Sticky day header: top: 0 relative to the scroll container (time-grid-wrapper) */
.time-grid-on .weekly-day-header { .time-grid-on .weekly-day-header {
top: calc(48px + env(safe-area-inset-top, 0px)) !important; position: sticky !important;
top: 0 !important;
z-index: 40 !important;
background-color: var(--weekly-bg, white);
} }
/* On mobile, time-column-header should NOT be sticky — it wastes vertical space */ /* On mobile, time-column-header should NOT be sticky — it wastes vertical space */
.time-grid-on .time-column-header { .time-grid-on .time-column-header {

View File

@ -14,7 +14,17 @@ import TaskRecurrenceModal from "./RecurrenceModal";
import { GridTaskBlock } from "./GridTaskBlock"; import { GridTaskBlock } from "./GridTaskBlock";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faApple, faGoogle, faMicrosoft } from "@fortawesome/free-brands-svg-icons"; import { faApple, faGoogle, faMicrosoft } from "@fortawesome/free-brands-svg-icons";
import { faServer } from "@fortawesome/free-solid-svg-icons"; import {
faServer, faFolder, faBriefcase, faBullseye, faRocket, faStar,
faLightbulb, faFire, faPalette, faMusic, faMobileScreen, faLaptop,
faGlobe, faHouse, faBuilding, faChartBar, faChartLine, faWrench,
faBolt, faGamepad, faPen, faBook, faGraduationCap, faFlask,
faMicroscope, faDumbbell, faUtensils, faPlane, faLeaf, faHeart,
faCartShopping, faCoins, faGift, faCamera, faFilm, faBroom,
faPaw, faEarthAmericas, faLock, faCheck, faCode, faCube,
faUsers, faCar, faMountain, faUmbrella, faClock, faTag,
IconDefinition,
} from "@fortawesome/free-solid-svg-icons";
import FocusModeOverlay from "./FocusModeOverlay"; import FocusModeOverlay from "./FocusModeOverlay";
import { import {
@ -5762,9 +5772,9 @@ export default function WeeklyView() {
{/* Mobile Header */} {/* Mobile Header */}
{isMobile && ( {isMobile && (
<header className="mobile-header" style={{ background: darkMode ? "#111827" : "#ffffff", borderBottom: `1px solid ${darkMode ? "#374151" : "#e5e7eb"}` }}> <header className="mobile-header" style={{ background: darkMode ? "#111827" : "#ffffff", borderBottom: `1px solid ${darkMode ? "#374151" : "#e5e7eb"}` }}>
{/* Left: Menu button */} {/* Left: Quick settings */}
<button className="mobile-header-btn" onClick={() => setShowSettings(true)} title="Menu" style={{ color: darkMode ? "#e5e7eb" : "#6b7280" }}> <button className="mobile-header-btn" onClick={() => setShowQuickSettings(true)} title="Quick Settings" style={{ color: darkMode ? "#e5e7eb" : "#6b7280" }}>
<Menu size={22} /> <PanelLeftOpen size={22} />
</button> </button>
{/* Center: Date display (tap to jump to date) */} {/* Center: Date display (tap to jump to date) */}
@ -5802,9 +5812,9 @@ export default function WeeklyView() {
<ChevronDown size={12} style={{ opacity: 0.5 }} /> <ChevronDown size={12} style={{ opacity: 0.5 }} />
</button> </button>
{/* Right: Search */} {/* Right: Settings menu */}
<button className="mobile-header-btn" onClick={() => setIsSearchOpen(true)} title="Search" style={{ color: darkMode ? "#e5e7eb" : "#6b7280" }}> <button className="mobile-header-btn" onClick={() => setShowSettings(true)} title="Settings" style={{ color: darkMode ? "#e5e7eb" : "#6b7280" }}>
<Search size={20} /> <Menu size={22} />
</button> </button>
</header> </header>
)} )}
@ -6345,7 +6355,7 @@ export default function WeeklyView() {
> >
<option value="">{t.filterByProject}</option> <option value="">{t.filterByProject}</option>
{projects.map(p => ( {projects.map(p => (
<option key={p.id} value={p.id}>{p.icon || "📁"} {p.name}</option> <option key={p.id} value={p.id}> {p.name}</option>
))} ))}
</select> </select>
<select <select
@ -6861,9 +6871,9 @@ export default function WeeklyView() {
if (!w) return null; if (!w) return null;
const icon = getWeatherIcon(w.code); const icon = getWeatherIcon(w.code);
return ( return (
<div style={{ position: "absolute", top: "1px", right: "3px", display: "flex", alignItems: "center", gap: "2px", fontSize: "10px", opacity: 0.6, pointerEvents: "none", zIndex: 1, lineHeight: 1 }}> <div style={{ position: "absolute", top: "1px", right: "3px", display: "flex", alignItems: "center", gap: "2px", fontSize: "10px", opacity: 0.7, pointerEvents: "none", zIndex: 1, lineHeight: 1, color: "#555" }}>
<span>{icon}</span> <span>{icon}</span>
<span style={{ fontWeight: 500 }}>{w.temp}°</span> <span style={{ fontWeight: 600, color: darkMode ? "#aaa" : "#555" }}>{w.temp}°</span>
</div> </div>
); );
})()} })()}
@ -9274,7 +9284,7 @@ function TaskItem({
setShowProjectPicker(false); setShowProjectPicker(false);
}} }}
> >
<span style={{ fontSize: "12px" }}>{p.icon || "📁"}</span> <span style={{ fontSize: "12px" }}><ProjectIcon icon={p.icon} size={12} color={p.color || "#999"} /></span>
<Circle size={8} fill={p.color || "#999"} stroke={p.color || "#999"} strokeWidth={0} /> <Circle size={8} fill={p.color || "#999"} stroke={p.color || "#999"} strokeWidth={0} />
{p.name} {p.name}
</button> </button>
@ -9500,13 +9510,43 @@ function TaskItem({
} }
// Projects Sidebar Component // Projects Sidebar Component
const projectEmojisGlobal = [ const projectIconsGlobal: { name: string; icon: IconDefinition }[] = [
"📁", "📂", "💼", "🎯", "🚀", "⭐", "💡", "🔥", "🎨", "🎵", { name: "folder", icon: faFolder }, { name: "briefcase", icon: faBriefcase },
"📱", "💻", "🌐", "🏠", "🏢", "📊", "📈", "🔧", "⚡", "🎮", { name: "bullseye", icon: faBullseye }, { name: "rocket", icon: faRocket },
"📝", "📖", "🎓", "🧪", "🔬", "🏋️", "🍽️", "✈️", "🌿", "❤️", { name: "star", icon: faStar }, { name: "lightbulb", icon: faLightbulb },
"🛒", "💰", "🎁", "📸", "🎬", "🧹", "🐾", "🌍", "🔒", "✅", { name: "fire", icon: faFire }, { name: "palette", icon: faPalette },
{ name: "music", icon: faMusic }, { name: "mobile", icon: faMobileScreen },
{ name: "laptop", icon: faLaptop }, { name: "globe", icon: faGlobe },
{ name: "house", icon: faHouse }, { name: "building", icon: faBuilding },
{ name: "chart-bar", icon: faChartBar }, { name: "chart-line", icon: faChartLine },
{ name: "wrench", icon: faWrench }, { name: "bolt", icon: faBolt },
{ name: "gamepad", icon: faGamepad }, { name: "pen", icon: faPen },
{ name: "book", icon: faBook }, { name: "graduation-cap", icon: faGraduationCap },
{ name: "flask", icon: faFlask }, { name: "microscope", icon: faMicroscope },
{ name: "dumbbell", icon: faDumbbell }, { name: "utensils", icon: faUtensils },
{ name: "plane", icon: faPlane }, { name: "leaf", icon: faLeaf },
{ name: "heart", icon: faHeart }, { name: "cart-shopping", icon: faCartShopping },
{ name: "coins", icon: faCoins }, { name: "gift", icon: faGift },
{ name: "camera", icon: faCamera }, { name: "film", icon: faFilm },
{ name: "broom", icon: faBroom }, { name: "paw", icon: faPaw },
{ name: "earth", icon: faEarthAmericas }, { name: "lock", icon: faLock },
{ name: "check", icon: faCheck }, { name: "code", icon: faCode },
{ name: "cube", icon: faCube }, { name: "users", icon: faUsers },
{ name: "car", icon: faCar }, { name: "mountain", icon: faMountain },
{ name: "umbrella", icon: faUmbrella }, { name: "clock", icon: faClock },
{ name: "tag", icon: faTag },
]; ];
// Helper to render a project icon (FA name or legacy emoji)
function ProjectIcon({ icon, size = 18, color }: { icon?: string | null; size?: number; color?: string }) {
if (!icon) return <FontAwesomeIcon icon={faFolder} style={{ fontSize: size, color }} />;
// Check if it's an FA icon name
const found = projectIconsGlobal.find((i) => i.name === icon);
if (found) return <FontAwesomeIcon icon={found.icon} style={{ fontSize: size, color }} />;
// Legacy emoji fallback
return <span style={{ fontSize: size, lineHeight: 1 }}>{icon}</span>;
}
function ProjectsSidebar({ darkMode, language, projects, onProjectsChanged, onClose }: { function ProjectsSidebar({ darkMode, language, projects, onProjectsChanged, onClose }: {
darkMode: boolean; darkMode: boolean;
language: string; language: string;
@ -9516,12 +9556,12 @@ function ProjectsSidebar({ darkMode, language, projects, onProjectsChanged, onCl
}) { }) {
const [newProjectName, setNewProjectName] = useState(""); const [newProjectName, setNewProjectName] = useState("");
const [newProjectColor, setNewProjectColor] = useState("#3b82f6"); const [newProjectColor, setNewProjectColor] = useState("#3b82f6");
const [newProjectIcon, setNewProjectIcon] = useState("📁"); const [newProjectIcon, setNewProjectIcon] = useState("folder");
const [showNewIconPicker, setShowNewIconPicker] = useState(false); const [showNewIconPicker, setShowNewIconPicker] = useState(false);
const [editingId, setEditingId] = useState<string | null>(null); const [editingId, setEditingId] = useState<string | null>(null);
const [editName, setEditName] = useState(""); const [editName, setEditName] = useState("");
const [editColor, setEditColor] = useState(""); const [editColor, setEditColor] = useState("");
const [editIcon, setEditIcon] = useState(""); const [editIcon, setEditIcon] = useState("folder");
const [showEditIconPicker, setShowEditIconPicker] = useState(false); const [showEditIconPicker, setShowEditIconPicker] = useState(false);
const t = { const t = {
@ -9539,7 +9579,7 @@ function ProjectsSidebar({ darkMode, language, projects, onProjectsChanged, onCl
const createProject = () => { const createProject = () => {
if (!newProjectName.trim()) return; if (!newProjectName.trim()) return;
fetch("/api/projects", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: newProjectName.trim(), color: newProjectColor, icon: newProjectIcon }) }) fetch("/api/projects", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: newProjectName.trim(), color: newProjectColor, icon: newProjectIcon }) })
.then(() => { onProjectsChanged(); setNewProjectName(""); setNewProjectIcon("📁"); }); .then(() => { onProjectsChanged(); setNewProjectName(""); setNewProjectIcon("folder"); });
}; };
const updateProject = (id: string) => { const updateProject = (id: string) => {
@ -9579,7 +9619,7 @@ function ProjectsSidebar({ darkMode, language, projects, onProjectsChanged, onCl
{projects.length === 0 ? ( {projects.length === 0 ? (
<div style={{ textAlign: "center", padding: "32px 16px" }}> <div style={{ textAlign: "center", padding: "32px 16px" }}>
<div style={{ width: "56px", height: "56px", borderRadius: "50%", background: darkMode ? "#2a2a3a" : "#f3f4f6", display: "flex", alignItems: "center", justifyContent: "center", margin: "0 auto 12px", fontSize: "24px" }}>📁</div> <div style={{ width: "56px", height: "56px", borderRadius: "50%", background: darkMode ? "#2a2a3a" : "#f3f4f6", display: "flex", alignItems: "center", justifyContent: "center", margin: "0 auto 12px" }}><ProjectIcon icon="folder" size={24} color={darkMode ? "#9ca3af" : "#999"} /></div>
<p style={{ fontSize: "0.9rem", color: "#aaa", fontStyle: "italic" }}>{t.noProjects}</p> <p style={{ fontSize: "0.9rem", color: "#aaa", fontStyle: "italic" }}>{t.noProjects}</p>
</div> </div>
) : ( ) : (
@ -9589,15 +9629,15 @@ function ProjectsSidebar({ darkMode, language, projects, onProjectsChanged, onCl
<div style={{ display: "flex", flexDirection: "column", gap: "10px", width: "100%" }}> <div style={{ display: "flex", flexDirection: "column", gap: "10px", width: "100%" }}>
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}> <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<div style={{ position: "relative" }}> <div style={{ position: "relative" }}>
<button onClick={() => setShowEditIconPicker(!showEditIconPicker)} style={{ width: "40px", height: "40px", borderRadius: "10px", border: `1px solid ${darkMode ? "#555" : "#e5e7eb"}`, background: darkMode ? "#1e1e2e" : "#fff", cursor: "pointer", fontSize: "20px", display: "flex", alignItems: "center", justifyContent: "center" }}> <button onClick={() => setShowEditIconPicker(!showEditIconPicker)} style={{ width: "40px", height: "40px", borderRadius: "10px", border: `1px solid ${darkMode ? "#555" : "#e5e7eb"}`, background: darkMode ? "#1e1e2e" : "#fff", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center" }}>
{editIcon || "📁"} <ProjectIcon icon={editIcon} size={18} color={darkMode ? "#d1d5db" : "#555"} />
</button> </button>
{showEditIconPicker && ( {showEditIconPicker && (
<div style={{ position: "absolute", top: "100%", left: 0, marginTop: "4px", zIndex: 50, background: darkMode ? "#2a2a3a" : "#fff", border: `1px solid ${darkMode ? "#555" : "#e5e7eb"}`, borderRadius: "12px", padding: "10px", boxShadow: "0 8px 24px rgba(0,0,0,0.2)", width: "240px" }}> <div style={{ position: "absolute", top: "100%", left: 0, marginTop: "4px", zIndex: 50, background: darkMode ? "#2a2a3a" : "#fff", border: `1px solid ${darkMode ? "#555" : "#e5e7eb"}`, borderRadius: "12px", padding: "10px", boxShadow: "0 8px 24px rgba(0,0,0,0.2)", width: "280px", maxHeight: "240px", overflowY: "auto" }}>
<div style={{ display: "grid", gridTemplateColumns: "repeat(8, 1fr)", gap: "4px" }}> <div style={{ display: "grid", gridTemplateColumns: "repeat(8, 1fr)", gap: "4px" }}>
{projectEmojisGlobal.map((emoji) => ( {projectIconsGlobal.map((item) => (
<button key={emoji} onClick={() => { setEditIcon(emoji); setShowEditIconPicker(false); }} style={{ width: "28px", height: "28px", border: "none", background: editIcon === emoji ? (darkMode ? "#374151" : "#f3f4f6") : "transparent", borderRadius: "6px", cursor: "pointer", fontSize: "16px", display: "flex", alignItems: "center", justifyContent: "center" }}> <button key={item.name} onClick={() => { setEditIcon(item.name); setShowEditIconPicker(false); }} style={{ width: "30px", height: "30px", border: "none", background: editIcon === item.name ? (darkMode ? "#374151" : "#e0e7ff") : "transparent", borderRadius: "6px", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", color: darkMode ? "#d1d5db" : "#555" }}>
{emoji} <FontAwesomeIcon icon={item.icon} style={{ fontSize: "14px" }} />
</button> </button>
))} ))}
</div> </div>
@ -9616,11 +9656,11 @@ function ProjectsSidebar({ darkMode, language, projects, onProjectsChanged, onCl
</div> </div>
) : ( ) : (
<> <>
<span style={{ fontSize: "22px", lineHeight: 1 }}>{p.icon || "📁"}</span> <ProjectIcon icon={p.icon} size={20} color={p.color || "#999"} />
<div style={{ flex: 1, minWidth: 0 }}> <div style={{ flex: 1, minWidth: 0 }}>
<span style={{ fontSize: "0.9rem", fontWeight: 600, display: "block", color: darkMode ? "#f0f0f0" : "#333" }}>{p.name}</span> <span style={{ fontSize: "0.9rem", fontWeight: 600, display: "block", color: darkMode ? "#f0f0f0" : "#333" }}>{p.name}</span>
</div> </div>
<button onClick={() => { setEditingId(p.id); setEditName(p.name); setEditColor(p.color || "#999"); setEditIcon(p.icon || "📁"); setShowEditIconPicker(false); }} style={{ padding: "6px", opacity: 0.5, cursor: "pointer", background: "none", border: "none", borderRadius: "6px", color: darkMode ? "#ccc" : "#333" }} title="Edit"> <button onClick={() => { setEditingId(p.id); setEditName(p.name); setEditColor(p.color || "#999"); setEditIcon(p.icon || "folder"); setShowEditIconPicker(false); }} style={{ padding: "6px", opacity: 0.5, cursor: "pointer", background: "none", border: "none", borderRadius: "6px", color: darkMode ? "#ccc" : "#333" }} title="Edit">
<Pencil size={15} /> <Pencil size={15} />
</button> </button>
<button onClick={() => deleteProject(p.id, p.name)} style={{ padding: "6px", opacity: 0.5, cursor: "pointer", color: "#ef4444", background: "none", border: "none", borderRadius: "6px" }} title="Delete"> <button onClick={() => deleteProject(p.id, p.name)} style={{ padding: "6px", opacity: 0.5, cursor: "pointer", color: "#ef4444", background: "none", border: "none", borderRadius: "6px" }} title="Delete">
@ -9638,15 +9678,15 @@ function ProjectsSidebar({ darkMode, language, projects, onProjectsChanged, onCl
<p style={{ fontSize: "0.8rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#888", marginBottom: "10px" }}>{t.addProject}</p> <p style={{ fontSize: "0.8rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#888", marginBottom: "10px" }}>{t.addProject}</p>
<div style={{ display: "flex", gap: "8px", alignItems: "center" }}> <div style={{ display: "flex", gap: "8px", alignItems: "center" }}>
<div style={{ position: "relative" }}> <div style={{ position: "relative" }}>
<button onClick={() => setShowNewIconPicker(!showNewIconPicker)} style={{ width: "40px", height: "40px", borderRadius: "10px", border: `1px solid ${darkMode ? "#555" : "#e5e7eb"}`, background: darkMode ? "#2a2a3a" : "#fff", cursor: "pointer", fontSize: "20px", display: "flex", alignItems: "center", justifyContent: "center" }}> <button onClick={() => setShowNewIconPicker(!showNewIconPicker)} style={{ width: "40px", height: "40px", borderRadius: "10px", border: `1px solid ${darkMode ? "#555" : "#e5e7eb"}`, background: darkMode ? "#2a2a3a" : "#fff", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center" }}>
{newProjectIcon} <ProjectIcon icon={newProjectIcon} size={18} color={darkMode ? "#d1d5db" : "#555"} />
</button> </button>
{showNewIconPicker && ( {showNewIconPicker && (
<div style={{ position: "absolute", bottom: "100%", left: 0, marginBottom: "4px", zIndex: 50, background: darkMode ? "#2a2a3a" : "#fff", border: `1px solid ${darkMode ? "#555" : "#e5e7eb"}`, borderRadius: "12px", padding: "10px", boxShadow: "0 8px 24px rgba(0,0,0,0.2)", width: "240px" }}> <div style={{ position: "absolute", bottom: "100%", left: 0, marginBottom: "4px", zIndex: 50, background: darkMode ? "#2a2a3a" : "#fff", border: `1px solid ${darkMode ? "#555" : "#e5e7eb"}`, borderRadius: "12px", padding: "10px", boxShadow: "0 8px 24px rgba(0,0,0,0.2)", width: "280px", maxHeight: "240px", overflowY: "auto" }}>
<div style={{ display: "grid", gridTemplateColumns: "repeat(8, 1fr)", gap: "4px" }}> <div style={{ display: "grid", gridTemplateColumns: "repeat(8, 1fr)", gap: "4px" }}>
{projectEmojisGlobal.map((emoji) => ( {projectIconsGlobal.map((item) => (
<button key={emoji} onClick={() => { setNewProjectIcon(emoji); setShowNewIconPicker(false); }} style={{ width: "28px", height: "28px", border: "none", background: newProjectIcon === emoji ? (darkMode ? "#374151" : "#f3f4f6") : "transparent", borderRadius: "6px", cursor: "pointer", fontSize: "16px", display: "flex", alignItems: "center", justifyContent: "center" }}> <button key={item.name} onClick={() => { setNewProjectIcon(item.name); setShowNewIconPicker(false); }} style={{ width: "30px", height: "30px", border: "none", background: newProjectIcon === item.name ? (darkMode ? "#374151" : "#e0e7ff") : "transparent", borderRadius: "6px", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", color: darkMode ? "#d1d5db" : "#555" }}>
{emoji} <FontAwesomeIcon icon={item.icon} style={{ fontSize: "14px" }} />
</button> </button>
))} ))}
</div> </div>
@ -10128,13 +10168,6 @@ function SettingsSidebar({
const [showEditProjectIconPicker, setShowEditProjectIconPicker] = useState(false); const [showEditProjectIconPicker, setShowEditProjectIconPicker] = useState(false);
const [weatherSearchResults, setWeatherSearchResults] = useState<any[]>([]); const [weatherSearchResults, setWeatherSearchResults] = useState<any[]>([]);
const projectEmojis = [
"📁", "📂", "💼", "🎯", "🚀", "⭐", "💡", "🔥", "🎨", "🎵",
"📱", "💻", "🌐", "🏠", "🏢", "📊", "📈", "🔧", "⚡", "🎮",
"📝", "📖", "🎓", "🧪", "🔬", "🏋️", "🍽️", "✈️", "🌿", "❤️",
"🛒", "💰", "🎁", "📸", "🎬", "🧹", "🐾", "🌍", "🔒", "✅",
];
// Fetch lists when the calendar tab is selected // Fetch lists when the calendar tab is selected
useEffect(() => { useEffect(() => {
if (activeTab === "calendar") { if (activeTab === "calendar") {