Refactor: Rearrange header, streamline settings, fix task hover icons, and overhaul recurring task manager
This commit is contained in:
parent
0e854b6e9b
commit
a834b440a5
@ -790,18 +790,20 @@ h3 {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
padding: 2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: var(--weekly-text-light);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: color 0.15s;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.task-action-btn:hover {
|
||||
color: var(--weekly-text);
|
||||
background: rgba(0,0,0,0.05);
|
||||
background: rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
/* Specific button styles */
|
||||
|
||||
@ -1,96 +1,107 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import React from 'react';
|
||||
import { Repeat, X, Trash2 } from 'lucide-react';
|
||||
|
||||
interface RecurringTasksManagerProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
tasks: any[];
|
||||
onStopRecurring: (task: any) => void;
|
||||
}
|
||||
|
||||
export default function RecurringTasksManager({ isOpen, onClose, tasks }: RecurringTasksManagerProps) {
|
||||
export default function RecurringTasksManager({ isOpen, onClose, tasks, onStopRecurring }: RecurringTasksManagerProps) {
|
||||
if (!isOpen) return null;
|
||||
|
||||
// Filter tasks that are recurring
|
||||
const recurringTasks = tasks.filter(t => t.isRecurring);
|
||||
// Filter and group recurring tasks by series signature
|
||||
// We use title + recurrence settings as the identity of a "series"
|
||||
const seriesGroups = new Map<string, any>();
|
||||
|
||||
tasks.filter(t => t.isRecurring).forEach(t => {
|
||||
// Only include those that aren't already ended
|
||||
if (t.recurrenceEndDate && new Date(t.recurrenceEndDate) < new Date()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const signature = `${t.title}-${t.recurrenceInterval || 1}-${t.recurrenceUnit || 'weeks'}-${t.recurrenceTime || ''}`;
|
||||
|
||||
// Keep the latest instance to represent the series
|
||||
const existing = seriesGroups.get(signature);
|
||||
if (!existing || (t.scheduledDate && new Date(t.scheduledDate) > new Date(existing.scheduledDate || 0))) {
|
||||
seriesGroups.set(signature, t);
|
||||
}
|
||||
});
|
||||
|
||||
const groupedTasks = Array.from(seriesGroups.values());
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4" onClick={onClose}>
|
||||
<div className="bg-white rounded-lg shadow-2xl w-[800px] max-w-full h-[600px] flex flex-col" onClick={e => e.stopPropagation()}>
|
||||
<div className="p-6 flex items-center justify-between border-b border-gray-100">
|
||||
<h2 className="text-xl font-bold text-gray-800">Recurring to-dos</h2>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 font-bold text-xl">
|
||||
×
|
||||
<div className="fixed inset-0 bg-black/50 z-[3000] flex items-center justify-center p-4 animate-fadeIn" onClick={onClose}>
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl shadow-2xl w-[600px] max-w-full overflow-hidden flex flex-col animate-scaleIn" onClick={e => e.stopPropagation()}>
|
||||
<div className="p-5 flex items-center justify-between border-b border-gray-100 dark:border-gray-800">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 bg-teal-100 dark:bg-teal-900/30 rounded-lg text-teal-600">
|
||||
<Repeat size={20} />
|
||||
</div>
|
||||
<h2 className="text-lg font-bold text-gray-800 dark:text-white">Wiederkehrende Aufgaben</h2>
|
||||
</div>
|
||||
<button onClick={onClose} className="p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-full text-gray-400 transition-colors">
|
||||
<X size={20} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto p-8">
|
||||
{recurringTasks.length === 0 ? (
|
||||
<div className="flex flex-col items-start max-w-lg">
|
||||
<p className="text-gray-600 mb-6">
|
||||
You don't have any recurring to-dos yet.
|
||||
<div className="flex-1 overflow-y-auto p-6">
|
||||
{groupedTasks.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-12 text-center">
|
||||
<div className="w-16 h-16 bg-gray-50 dark:bg-gray-800 rounded-full flex items-center justify-center text-gray-300 mb-4">
|
||||
<Repeat size={32} />
|
||||
</div>
|
||||
<p className="text-gray-500 dark:text-gray-400 max-w-xs">
|
||||
Du hast momentan keine aktiven wiederkehrenden Aufgaben.
|
||||
</p>
|
||||
|
||||
<button className="bg-[#333] text-white px-4 py-2 rounded text-sm font-medium flex items-center gap-2 hover:bg-black transition-colors mb-8">
|
||||
<span>+</span> Recurring to-do
|
||||
</button>
|
||||
|
||||
<p className="text-gray-800 font-medium mb-4">Or, type a frequency to the end of a to-do.</p>
|
||||
|
||||
<div className="bg-gray-100 p-8 rounded mb-8 w-full flex items-center justify-center">
|
||||
<div className="bg-white p-6 shadow-sm rounded text-center w-64">
|
||||
<div className="text-[10px] font-bold text-gray-400 uppercase tracking-widest mb-1">OCT 5, 2023</div>
|
||||
<div className="text-xl font-bold text-gray-900 uppercase tracking-wider mb-4">THURSDAY</div>
|
||||
<div className="h-6 w-1 bg-black animate-pulse mx-auto"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-8 text-sm w-full">
|
||||
<div>
|
||||
<h4 className="italic text-gray-500 mb-2">Frequencies:</h4>
|
||||
<ul className="text-gray-500 italic space-y-1">
|
||||
<li>every day</li>
|
||||
<li>every week</li>
|
||||
<li>every other week</li>
|
||||
<li>every month</li>
|
||||
<li>every year</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="italic text-gray-500 mb-2">Examples:</h4>
|
||||
<ul className="text-gray-500 italic space-y-1">
|
||||
<li>Brush teeth every day</li>
|
||||
<li>Management meeting every week</li>
|
||||
<li>Piano lessons every other week</li>
|
||||
<li>Pay rent every month</li>
|
||||
<li>Garret's birthday every year</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{recurringTasks.map(task => (
|
||||
<div key={task.id} className="flex items-center justify-between p-4 bg-gray-50 rounded border border-gray-100">
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">{task.title}</div>
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
Repeats every {task.recurrenceInterval} {task.recurrenceUnit}
|
||||
<div className="space-y-3">
|
||||
{groupedTasks.map(task => (
|
||||
<div key={task.id} className="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800/50 rounded-xl border border-gray-100 dark:border-gray-700/50 hover:border-teal-200 dark:hover:border-teal-900/50 transition-all">
|
||||
<div className="flex-1 min-w-0 pr-4">
|
||||
<div className="font-semibold text-gray-900 dark:text-white truncate" title={task.title}>
|
||||
{task.title}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="text-xs px-2 py-0.5 bg-teal-50 dark:bg-teal-900/20 text-teal-600 dark:text-teal-400 rounded-md font-medium capitalize">
|
||||
Alle {task.recurrenceInterval === 1 ? '' : task.recurrenceInterval} {
|
||||
task.recurrenceUnit === 'days' ? 'Tage' :
|
||||
task.recurrenceUnit === 'weeks' ? 'Wochen' :
|
||||
task.recurrenceUnit === 'months' ? 'Monate' : 'Jahre'
|
||||
}
|
||||
</span>
|
||||
{task.recurrenceTime && (
|
||||
<span className="text-[10px] text-gray-400">
|
||||
um {task.recurrenceTime} Uhr
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<button className="text-red-500 text-sm hover:underline">Stop Details</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
if (confirm(`Serie "${task.title}" wirklich beenden?`)) {
|
||||
onStopRecurring(task);
|
||||
}
|
||||
}}
|
||||
className="flex items-center gap-2 px-3 py-2 bg-red-50 hover:bg-red-100 dark:bg-red-900/10 dark:hover:bg-red-900/20 text-red-600 dark:text-red-400 rounded-lg text-xs font-bold transition-colors group"
|
||||
>
|
||||
<Trash2 size={14} className="group-hover:scale-110 transition-transform" />
|
||||
Serie beenden
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="p-4 border-t border-gray-100 flex justify-end">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="w-10 h-10 bg-[#333] rounded-full flex items-center justify-center text-white hover:bg-black transition-colors text-xl font-bold"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
<div className="p-3 bg-gray-50 dark:bg-gray-900 border-t border-gray-100 dark:border-gray-800 flex justify-center">
|
||||
<p className="text-[10px] text-gray-400 font-medium">
|
||||
Tipp: Du kannst Aufgaben jederzeit beim Bearbeiten wiederkehrend machen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -323,7 +323,6 @@ export default function WeeklyView() {
|
||||
const [somedayLists, setSomedayLists] = useState<SomedayList[]>([]);
|
||||
const [editingTaskId, setEditingTaskId] = useState<string | null>(null);
|
||||
const [showSettings, setShowSettings] = useState(false);
|
||||
const [showPreferences, setShowPreferences] = useState(false);
|
||||
const [showDatePicker, setShowDatePicker] = useState(false);
|
||||
const [isAddingSomedayList, setIsAddingSomedayList] = useState(false);
|
||||
const [newSomedayListName, setNewSomedayListName] = useState('');
|
||||
@ -1522,16 +1521,32 @@ export default function WeeklyView() {
|
||||
|
||||
{/* Refactored Header: Left, Center, Right */}
|
||||
<header className="flex items-center justify-between w-full px-4 py-2 border-b border-gray-200 bg-white dark:bg-gray-900 dark:border-gray-700 dark:text-white transition-colors duration-200">
|
||||
{/* LEFT SECTION: Days to Show & Time Range */}
|
||||
{/* LEFT SECTION: Slot Duration & Days to Show */}
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Slot Duration */}
|
||||
{showTimeGrid && (
|
||||
<div className="flex items-center gap-1 bg-gray-100 dark:bg-gray-800 rounded p-1" title="Slot Duration">
|
||||
<Clock size={16} className="text-gray-500 mr-1" />
|
||||
{[15, 30, 60].map(duration => (
|
||||
<button
|
||||
key={duration}
|
||||
onClick={() => setCellDuration(duration as CellDuration)}
|
||||
className={`px-2 py-0.5 text-xs rounded transition-colors ${cellDuration === duration ? 'bg-white shadow-sm font-bold text-black' : 'text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700'}`}
|
||||
>
|
||||
{duration}m
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Days to Show */}
|
||||
<div className="flex items-center gap-1 bg-gray-100 rounded p-1" title="Days to show">
|
||||
<div className="flex items-center gap-1 bg-gray-100 dark:bg-gray-800 rounded p-1" title="Days to show">
|
||||
<LayoutGrid size={16} className="text-gray-500 mr-1" />
|
||||
{[1, 3, 5, 7].map(num => (
|
||||
<button
|
||||
key={num}
|
||||
onClick={() => setViewDays(num)}
|
||||
className={`px-2 py-0.5 text-xs rounded transition-colors ${viewDays === num ? 'bg-white shadow-sm font-bold text-black' : 'text-gray-500 hover:text-gray-900 hover:bg-gray-200'}`}
|
||||
className={`px-2 py-0.5 text-xs rounded transition-colors ${viewDays === num ? 'bg-white shadow-sm font-bold text-black' : 'text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700'}`}
|
||||
>
|
||||
{num}
|
||||
</button>
|
||||
@ -1540,7 +1555,7 @@ export default function WeeklyView() {
|
||||
|
||||
{/* Time Range */}
|
||||
{showTimeGrid && (
|
||||
<div className="flex items-center gap-2 text-xs text-gray-500 bg-gray-100 rounded p-1 px-2" title="Visible Hours">
|
||||
<div className="flex items-center gap-2 text-xs text-gray-500 bg-gray-100 dark:bg-gray-800 rounded p-1 px-2" title="Visible Hours">
|
||||
<Clock size={16} className="text-gray-500" />
|
||||
<div className="flex items-center gap-1">
|
||||
<input
|
||||
@ -1549,7 +1564,7 @@ export default function WeeklyView() {
|
||||
max={endHour - 1}
|
||||
value={startHour}
|
||||
onChange={(e) => setStartHour(Math.max(0, Math.min(parseInt(e.target.value) || 0, endHour - 1)))}
|
||||
className="w-12 p-0.5 border border-gray-200 rounded text-center bg-transparent focus:outline-none focus:border-teal-500"
|
||||
className="w-10 p-0.5 border border-gray-200 dark:border-gray-700 rounded text-center bg-transparent focus:outline-none focus:border-teal-500"
|
||||
/>
|
||||
<span>-</span>
|
||||
<input
|
||||
@ -1558,20 +1573,33 @@ export default function WeeklyView() {
|
||||
max="24"
|
||||
value={endHour}
|
||||
onChange={(e) => setEndHour(Math.max(startHour + 1, Math.min(parseInt(e.target.value) || 24, 24)))}
|
||||
className="w-12 p-0.5 border border-gray-200 rounded text-center bg-transparent focus:outline-none focus:border-teal-500"
|
||||
className="w-10 p-0.5 border border-gray-200 dark:border-gray-700 rounded text-center bg-transparent focus:outline-none focus:border-teal-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Day/Night Mode Switch */}
|
||||
<button
|
||||
onClick={() => setDarkMode(!darkMode)}
|
||||
className="p-1.5 rounded-md hover:bg-gray-100 transition-colors"
|
||||
title={darkMode ? "Switch to Light Mode" : "Switch to Dark Mode"}
|
||||
>
|
||||
{darkMode ? <Sun size={18} className="text-yellow-500" /> : <Moon size={18} className="text-gray-500" />}
|
||||
</button>
|
||||
{/* View Style Toggles */}
|
||||
<div className="flex items-center gap-1 bg-gray-100 dark:bg-gray-800 rounded p-1" title="View Style">
|
||||
<button
|
||||
onClick={() => setShowTimeGrid(!showTimeGrid)}
|
||||
className={`px-2 py-0.5 text-xs rounded transition-colors ${showTimeGrid ? 'bg-white shadow-sm font-bold text-black' : 'text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700'}`}
|
||||
>
|
||||
Time Grid
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewStyle('grid')}
|
||||
className={`px-2 py-0.5 text-xs rounded transition-colors ${!showTimeGrid && viewStyle === 'grid' ? 'bg-white shadow-sm font-bold text-black' : 'text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700'}`}
|
||||
>
|
||||
Grid
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewStyle('list')}
|
||||
className={`px-2 py-0.5 text-xs rounded transition-colors ${!showTimeGrid && viewStyle === 'list' ? 'bg-white shadow-sm font-bold text-black' : 'text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700'}`}
|
||||
>
|
||||
List
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CENTER SECTION: Week/Year, Motto, Focus Mode */}
|
||||
@ -1638,27 +1666,14 @@ export default function WeeklyView() {
|
||||
|
||||
{/* RIGHT SECTION: Navigation & Tools */}
|
||||
<div className="flex items-center gap-3">
|
||||
{/* Date Picker Toggle */}
|
||||
<div className="relative">
|
||||
<button
|
||||
className={`p-1.5 hover:bg-gray-100 rounded-md transition-colors ${showDatePicker ? 'text-teal-600 bg-teal-50' : 'text-gray-500 hover:text-black'}`}
|
||||
onClick={() => setShowDatePicker(!showDatePicker)}
|
||||
title="Jump to date"
|
||||
>
|
||||
<Calendar size={18} />
|
||||
</button>
|
||||
{showDatePicker && (
|
||||
<SimpleDatePicker
|
||||
selected={currentWeekStart}
|
||||
onSelect={(date) => {
|
||||
setCurrentWeekStart(getStartOfWeek(date, weekStartDay));
|
||||
setShowDatePicker(false);
|
||||
}}
|
||||
onClose={() => setShowDatePicker(false)}
|
||||
language={language}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{/* Day/Night Mode Switch */}
|
||||
<button
|
||||
onClick={() => setDarkMode(!darkMode)}
|
||||
className="p-1.5 rounded-md hover:bg-gray-100 transition-colors"
|
||||
title={darkMode ? "Switch to Light Mode" : "Switch to Dark Mode"}
|
||||
>
|
||||
{darkMode ? <Sun size={18} className="text-yellow-500" /> : <Moon size={18} className="text-gray-500" />}
|
||||
</button>
|
||||
|
||||
{/* Navigation Controls */}
|
||||
<div className="flex items-center bg-gray-100 rounded-lg p-0.5">
|
||||
@ -1672,16 +1687,13 @@ export default function WeeklyView() {
|
||||
Today
|
||||
</button>
|
||||
<button className="p-1 hover:bg-white hover:shadow-sm rounded text-gray-500 hover:text-black transition-all" onClick={goToNextDay} title="Next Day">
|
||||
<ChevronRight size={16} />
|
||||
<ChevronLeft size={16} className="rotate-180" />
|
||||
</button>
|
||||
<button className="p-1 hover:bg-white hover:shadow-sm rounded text-gray-500 hover:text-black transition-all" onClick={goToNextWeek} title="Next Week">
|
||||
<ChevronsRight size={16} />
|
||||
<ChevronsLeft size={16} className="rotate-180" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="h-4 w-px bg-gray-300 mx-1"></div>
|
||||
|
||||
{/* Search */}
|
||||
<button
|
||||
className="p-1.5 hover:bg-gray-100 rounded-md text-gray-500 hover:text-black transition-colors"
|
||||
@ -1700,13 +1712,9 @@ export default function WeeklyView() {
|
||||
<Repeat size={18} />
|
||||
</button>
|
||||
|
||||
{/* Settings - Actually triggers User Menu in original code? No, settings was separate. */}
|
||||
{/* Original code had UserMenu handling settings. And a separate sync indicator. */}
|
||||
{/* The prompt asked for: Search | Settings | User Menu */}
|
||||
|
||||
<button
|
||||
className="p-1.5 hover:bg-gray-100 rounded-md text-gray-500 hover:text-black transition-colors"
|
||||
onClick={() => setShowPreferences(true)}
|
||||
onClick={() => setShowSettings(true)}
|
||||
title="Settings"
|
||||
>
|
||||
<Settings size={18} />
|
||||
@ -1715,7 +1723,7 @@ export default function WeeklyView() {
|
||||
{/* User Menu */}
|
||||
<UserMenu
|
||||
userEmail={session?.user?.email}
|
||||
onOpenSettings={() => setShowPreferences(true)}
|
||||
onOpenSettings={() => setShowSettings(true)}
|
||||
trigger={
|
||||
<button className="p-1.5 hover:bg-gray-100 rounded-md text-gray-500 hover:text-black transition-colors" title="User Menu">
|
||||
<User size={18} />
|
||||
@ -2533,7 +2541,38 @@ export default function WeeklyView() {
|
||||
<RecurringTasksManager
|
||||
isOpen={isRecurringTasksOpen}
|
||||
onClose={() => setIsRecurringTasksOpen(false)}
|
||||
tasks={tasks} // Pass tasks to manager
|
||||
tasks={tasks}
|
||||
onStopRecurring={async (task) => {
|
||||
const idToUpdate = task.id.startsWith('virtual-') ? task.id.split('-')[1] : task.id;
|
||||
const dateStr = new Date().toISOString();
|
||||
|
||||
// Update locally
|
||||
setTasks(prev => prev.map(t => {
|
||||
const isMatch = t.title === task.title &&
|
||||
t.userId === task.userId &&
|
||||
t.recurrenceInterval === task.recurrenceInterval &&
|
||||
t.recurrenceUnit === task.recurrenceUnit;
|
||||
if (isMatch) {
|
||||
return { ...t, recurrenceEndDate: new Date(dateStr) };
|
||||
}
|
||||
return t;
|
||||
}));
|
||||
|
||||
// Update DB
|
||||
try {
|
||||
await fetch('/api/tasks', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
id: idToUpdate,
|
||||
recurrenceEndDate: dateStr
|
||||
})
|
||||
});
|
||||
fetchTasks();
|
||||
} catch (error) {
|
||||
console.error('Failed to stop recurring series:', error);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/* Recurrence Modal */}
|
||||
{
|
||||
@ -2660,106 +2699,6 @@ export default function WeeklyView() {
|
||||
)
|
||||
}
|
||||
|
||||
{/* Preferences Slide-in Panel */}
|
||||
<div className={`preferences-panel ${showPreferences ? 'open' : ''}`}>
|
||||
<div className="preferences-panel-content">
|
||||
<h3 style={{ fontSize: '1.2rem', fontWeight: 400, marginBottom: '1.5rem', color: '#fff' }}>Preferences</h3>
|
||||
|
||||
{/* Columns (View Days) */}
|
||||
<div className="pref-row">
|
||||
<span className="pref-label">Columns</span>
|
||||
<div className="pref-options">
|
||||
{[1, 3, 5, 7].map(n => (
|
||||
<button key={n} className={`pref-option-btn ${viewDays === n ? 'active' : ''}`}
|
||||
onClick={() => setViewDays(n as any)}>{n}</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Show Time Grid */}
|
||||
<div className="pref-row">
|
||||
<span className="pref-label">Time grid</span>
|
||||
<div className="pref-options">
|
||||
<button className={`pref-toggle ${showTimeGrid ? 'active' : ''}`}
|
||||
onClick={() => setShowTimeGrid(!showTimeGrid)}>
|
||||
{showTimeGrid ? '◉' : '○'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showTimeGrid && (
|
||||
<div className="pref-row">
|
||||
<span className="pref-label">Slot duration</span>
|
||||
<div className="pref-options">
|
||||
{[15, 30, 60].map(n => (
|
||||
<button key={n} className={`pref-option-btn ${cellDuration === n ? 'active' : ''}`}
|
||||
onClick={() => setCellDuration(n as CellDuration)}>{n}m</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* View Style */}
|
||||
<div className="pref-row">
|
||||
<span className="pref-label">View style</span>
|
||||
<div className="pref-options">
|
||||
<button className={`pref-option-btn ${viewStyle === 'grid' ? 'active' : ''}`}
|
||||
onClick={() => setViewStyle('grid')}>Grid</button>
|
||||
<button className={`pref-option-btn ${viewStyle === 'list' ? 'active' : ''}`}
|
||||
onClick={() => setViewStyle('list')}>List</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Show Someday */}
|
||||
<div className="pref-row">
|
||||
<span className="pref-label">Someday</span>
|
||||
<div className="pref-options">
|
||||
<button className={`pref-toggle ${showSomeday ? 'active' : ''}`}
|
||||
onClick={() => setShowSomeday(!showSomeday)}>
|
||||
{showSomeday ? '◉' : '○'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Show All Day */}
|
||||
<div className="pref-row">
|
||||
<span className="pref-label">All-day events</span>
|
||||
<div className="pref-options">
|
||||
<button className={`pref-toggle ${showAllDay ? 'active' : ''}`}
|
||||
onClick={() => setShowAllDay(!showAllDay)}>
|
||||
{showAllDay ? '◉' : '○'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Lines */}
|
||||
<div className="pref-row">
|
||||
<span className="pref-label">Protect events</span>
|
||||
<div className="pref-options">
|
||||
<button className={`pref-toggle ${protectEventTimes ? 'active' : ''}`}
|
||||
onClick={() => setProtectEventTimes(!protectEventTimes)}>
|
||||
{protectEventTimes ? '◉' : '○'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ borderTop: '1px solid rgba(255,255,255,0.1)', margin: '1rem 0' }} />
|
||||
|
||||
{/* Full Settings Link */}
|
||||
<button
|
||||
className="pref-full-settings-btn"
|
||||
onClick={() => { setShowPreferences(false); setShowSettings(true); }}
|
||||
>
|
||||
⚙ Full Settings
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Close chevron */}
|
||||
<button className="preferences-close-btn" onClick={() => setShowPreferences(false)}>
|
||||
‹‹
|
||||
</button>
|
||||
</div>
|
||||
{showPreferences && <div className="preferences-overlay" onClick={() => setShowPreferences(false)} />}
|
||||
|
||||
{
|
||||
selectedTaskForNotes && (
|
||||
@ -3090,7 +3029,7 @@ function TaskItem({ task, isEditing, onToggle, onEdit, onUpdate, onDelete, onNot
|
||||
>
|
||||
{/* Edit */}
|
||||
<button className="task-action-btn" onClick={(e) => { e.stopPropagation(); onEdit(); }} title="Edit">
|
||||
<svg viewBox="0 0 24 24" width={variant === 'minimal' ? "14" : "16"} height={variant === 'minimal' ? "14" : "16"} stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
|
||||
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
|
||||
</button>
|
||||
|
||||
{/* Recurrence */}
|
||||
@ -3099,7 +3038,7 @@ function TaskItem({ task, isEditing, onToggle, onEdit, onUpdate, onDelete, onNot
|
||||
onClick={(e) => { e.stopPropagation(); onRecurrence(); }}
|
||||
title={task.isRecurring ? "Edit recurrence" : "Make recurring"}
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width={variant === 'minimal' ? "14" : "16"} height={variant === 'minimal' ? "14" : "16"} stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
||||
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="23 4 23 10 17 10"></polyline>
|
||||
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path>
|
||||
</svg>
|
||||
@ -3111,7 +3050,7 @@ function TaskItem({ task, isEditing, onToggle, onEdit, onUpdate, onDelete, onNot
|
||||
onClick={(e) => { e.stopPropagation(); setIsNotesOpen(!isNotesOpen); }}
|
||||
title="Notes"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width={variant === 'minimal' ? "14" : "16"} height={variant === 'minimal' ? "14" : "16"} stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
||||
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
@ -3125,7 +3064,7 @@ function TaskItem({ task, isEditing, onToggle, onEdit, onUpdate, onDelete, onNot
|
||||
onClick={(e) => { e.stopPropagation(); onRollToggle(); }}
|
||||
title={task.isRolling ? "Disable rolling" : "Enable rolling"}
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
||||
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="1 4 1 10 7 10"></polyline>
|
||||
<path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path>
|
||||
</svg>
|
||||
@ -3134,7 +3073,7 @@ function TaskItem({ task, isEditing, onToggle, onEdit, onUpdate, onDelete, onNot
|
||||
|
||||
{/* Delete */}
|
||||
<button className="task-action-btn delete" onClick={(e) => { e.stopPropagation(); onDelete(); }} title="Delete">
|
||||
<svg viewBox="0 0 24 24" width={variant === 'minimal' ? "14" : "16"} height={variant === 'minimal' ? "14" : "16"} stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
||||
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user