feat: Refactor view styles (Simple, Calendar, List) and global renaming
- Implemented Simple view (no borders, added spacing). - Updated Calendar view to Google Calendar style. - Renamed TeuxDeux view to List. - Replaced 'teuxdeux' branding globally. - Fixed horizontal scrollbars in Simple view. - Updated global border color.
This commit is contained in:
parent
72e62ead4a
commit
3e7b87428a
@ -449,7 +449,7 @@ h3 {
|
||||
--weekly-bg: #ffffff;
|
||||
--weekly-text: #000000;
|
||||
--weekly-text-light: #a6a6a7;
|
||||
--weekly-border: #eeeeee;
|
||||
--weekly-border: #cccccc;
|
||||
--weekly-completed: #cccccc;
|
||||
--weekly-font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
|
||||
@ -635,6 +635,7 @@ h3 {
|
||||
min-height: 400px;
|
||||
max-height: calc(100vh - 200px);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.weekly-day-column:first-child {
|
||||
@ -1726,6 +1727,7 @@ h3 {
|
||||
|
||||
.time-grid-wrapper .time-slots-container {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* All-Day Events Section */
|
||||
@ -2579,3 +2581,150 @@ h3 {
|
||||
font-size: 0.7rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* Header Icons */
|
||||
.weekly-btn-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0;
|
||||
border: 1px solid #e0e0e0;
|
||||
background-color: white;
|
||||
color: #555;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.weekly-btn-icon:hover {
|
||||
background-color: #f3f4f6;
|
||||
border-color: #d1d5db;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.weekly-btn-icon.active {
|
||||
background-color: #e5e7eb;
|
||||
border-color: #9ca3af;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.dark-mode .weekly-btn-icon {
|
||||
background-color: #1f2937;
|
||||
border-color: #374151;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.dark-mode .weekly-btn-icon:hover {
|
||||
background-color: #374151;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.dark-mode .weekly-btn-icon.active {
|
||||
background-color: #4b5563;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Header Hover Reveal */
|
||||
.group:hover .transition-opacity {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.group .transition-opacity {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* List View (formerly Grid/App View - no vertical lines) */
|
||||
.list-view.time-grid-off .weekly-day-column {
|
||||
border-right: none !important;
|
||||
}
|
||||
|
||||
/* Calendar View (Google Calendar Style - vertical lines) */
|
||||
.calendar-view.time-grid-on .weekly-day-column {
|
||||
border-right: 1px solid #e0e0e0;
|
||||
margin: 0 !important;
|
||||
}
|
||||
.dark-mode .calendar-view.time-grid-on .weekly-day-column {
|
||||
border-right: 1px solid #333;
|
||||
}
|
||||
|
||||
/* Simple View (Original Style with Spacing) */
|
||||
.simple-view .weekly-day-column {
|
||||
border-left: 0px !important;
|
||||
margin: 0 10px !important;
|
||||
overflow-x: hidden !important;
|
||||
}
|
||||
|
||||
.simple-view .time-column {
|
||||
border-right: 0px !important;
|
||||
}
|
||||
|
||||
.simple-view .time-slot-event {
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Scrollbar Hiding for All-Day Events */
|
||||
.all-day-events-column {
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
}
|
||||
|
||||
.all-day-events-column::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari, Opera */
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
/* Mobile/Tablet adjustment */
|
||||
.weekly-header {
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
padding: 10px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.weekly-header > div {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Fix overlap on mobile by removing absolute positioning */
|
||||
.weekly-header .absolute {
|
||||
position: static !important;
|
||||
transform: none !important;
|
||||
left: auto !important;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.weekly-btn-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
/* Stack days vertically on mobile */
|
||||
.list-view .weekly-days-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.time-grid-wrapper {
|
||||
flex-direction: column;
|
||||
height: calc(100vh - 120px); /* Adjust for header */
|
||||
}
|
||||
|
||||
.weekly-day-column {
|
||||
min-height: auto;
|
||||
border-right: none !important;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
/* Ensure sidebar is scrollable and accessible */
|
||||
.weekly-settings-sidebar {
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,7 +193,7 @@ export default function DatePicker({ selected, onSelect, onClose, language = 'en
|
||||
margin-top: 8px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
border-bottom: 5px solid #00ceb0; /* TeuxDeuxish Teal/Green */
|
||||
border-bottom: 5px solid #00ceb0; /* Weekly Teal/Green */
|
||||
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
|
||||
padding: 20px;
|
||||
z-index: 1000;
|
||||
|
||||
@ -92,7 +92,7 @@ export default function TaskItem({ task, onToggleComplete, onDelete, onEdit, onT
|
||||
<div className={containerClass}>
|
||||
<div className={`flex items-start ${variant === 'minimal' ? 'w-full' : ''}`}>
|
||||
<div className={`flex items-center mr-3 ${variant === 'minimal' ? '' : 'mt-1'} space-x-2`}>
|
||||
{/* Rolling Toggle - hide in minimal unless hovered or active? Or keep it? TeuxDeux usually has verify simple. Keep it for now. */}
|
||||
{/* Rolling Toggle - hide in minimal unless hovered or active? Or keep it? App usually has verify simple. Keep it for now. */}
|
||||
<button
|
||||
onClick={() => onToggleRolling(task.id)}
|
||||
className={`p-1 rounded-full hover:bg-gray-100 transition-colors ${task.isRolling ? 'text-blue-600' : 'text-gray-300'}`}
|
||||
|
||||
@ -22,7 +22,9 @@ import {
|
||||
Sun,
|
||||
Moon,
|
||||
Repeat,
|
||||
GripVertical
|
||||
GripVertical,
|
||||
Play,
|
||||
Zap
|
||||
} from 'lucide-react';
|
||||
|
||||
// Types
|
||||
@ -31,6 +33,8 @@ import SearchModal from './SearchModal';
|
||||
import SimpleDatePicker from './SimpleDatePicker';
|
||||
import RecurringTasksManager from './RecurringTasksManager';
|
||||
|
||||
export type ViewStyle = 'simple' | 'calendar' | 'list';
|
||||
|
||||
interface Task {
|
||||
id: string;
|
||||
title: string;
|
||||
@ -132,8 +136,9 @@ const translations: Record<string, any> = {
|
||||
showTimeGrid: 'Show Time Grid',
|
||||
timeSlotDuration: 'Time Slot Duration',
|
||||
viewStyle: 'View Style',
|
||||
gridView: 'Grid View',
|
||||
listView: 'List View',
|
||||
simpleView: 'Simple',
|
||||
calendarView: 'Calendar',
|
||||
listView: 'List',
|
||||
language: 'Language',
|
||||
dateFormat: 'Date Format',
|
||||
timeFormat: 'Time Format',
|
||||
@ -179,8 +184,9 @@ const translations: Record<string, any> = {
|
||||
showTimeGrid: 'Zeitplan anzeigen',
|
||||
timeSlotDuration: 'Zeitfensterdauer',
|
||||
viewStyle: 'Ansichtsstil',
|
||||
gridView: 'Rasteransicht',
|
||||
listView: 'Listenansicht',
|
||||
simpleView: 'Einfach',
|
||||
calendarView: 'Kalender',
|
||||
listView: 'Liste',
|
||||
language: 'Sprache',
|
||||
dateFormat: 'Datumsformat',
|
||||
timeFormat: 'Zeitformat',
|
||||
@ -483,7 +489,7 @@ export default function WeeklyView() {
|
||||
const [selectedTaskForNotes, setSelectedTaskForNotes] = useState<Task | null>(null);
|
||||
const [currentTime, setCurrentTime] = useState(new Date());
|
||||
const [dropPreview, setDropPreview] = useState<{ day: number; slot: string } | null>(null);
|
||||
const [viewStyle, setViewStyle] = useState<'grid' | 'list'>('list');
|
||||
const [viewStyle, setViewStyle] = useState<ViewStyle>('simple');
|
||||
const [protectEventTimes, setProtectEventTimes] = useState(true);
|
||||
const [unlockedEvents, setUnlockedEvents] = useState<Set<string>>(new Set());
|
||||
|
||||
@ -853,7 +859,7 @@ export default function WeeklyView() {
|
||||
if (data.user.startHour !== undefined) setStartHour(data.user.startHour);
|
||||
if (data.user.endHour !== undefined) setEndHour(data.user.endHour);
|
||||
if (data.user.viewStyle !== undefined) {
|
||||
setViewStyle(data.user.viewStyle as 'grid' | 'list');
|
||||
setViewStyle(data.user.viewStyle as ViewStyle);
|
||||
setShowTimeGrid(data.user.showTimeGrid ?? true);
|
||||
}
|
||||
if (data.user.viewDays !== undefined) setViewDays(data.user.viewDays);
|
||||
@ -900,7 +906,7 @@ export default function WeeklyView() {
|
||||
if (data.user.focusBreakDuration) setFocusBreakDuration(data.user.focusBreakDuration);
|
||||
if (data.user.showTimeGrid !== undefined) setShowTimeGrid(data.user.showTimeGrid);
|
||||
if (data.user.cellDuration) setCellDuration(data.user.cellDuration as CellDuration);
|
||||
if (data.user.viewStyle) setViewStyle(data.user.viewStyle as 'grid' | 'list');
|
||||
if (data.user.viewStyle) setViewStyle(data.user.viewStyle as ViewStyle);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@ -1846,7 +1852,7 @@ export default function WeeklyView() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`weekly-container ${darkMode ? 'dark-mode' : ''} font-size-${fontSize.toLowerCase()} ${viewStyle}-view`} style={containerStyle}>
|
||||
<div className={`weekly-container ${darkMode ? 'dark-mode' : ''} font-size-${fontSize.toLowerCase()} ${viewStyle}-view ${showTimeGrid ? 'time-grid-on' : 'time-grid-off'}`} style={containerStyle}>
|
||||
{/* View Transitions Style Block */}
|
||||
<style dangerouslySetInnerHTML={{
|
||||
__html: (() => {
|
||||
@ -1871,9 +1877,9 @@ 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">
|
||||
<header className="group 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: Slot Duration & Days to Show */}
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-4 transition-opacity duration-300 opacity-0 group-hover:opacity-100">
|
||||
{/* Slot Duration */}
|
||||
{showTimeGrid && (
|
||||
<div className="flex items-center gap-1 bg-gray-100 dark:bg-gray-800 rounded p-1" title="Slot Duration">
|
||||
@ -1948,32 +1954,32 @@ export default function WeeklyView() {
|
||||
<div className="flex items-center gap-1 bg-gray-100 dark:bg-gray-800 rounded p-1" title="View Style">
|
||||
<button
|
||||
onClick={() => {
|
||||
const newVal = !showTimeGrid;
|
||||
setShowTimeGrid(newVal);
|
||||
if (newVal) setViewStyle('list'); // Default to list if grid is enabled
|
||||
saveSetting('showTimeGrid', newVal);
|
||||
setViewStyle('simple');
|
||||
setShowTimeGrid(true);
|
||||
saveSetting('viewStyle', 'simple');
|
||||
saveSetting('showTimeGrid', true);
|
||||
}}
|
||||
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'}`}
|
||||
className={`px-2 py-0.5 text-xs rounded transition-colors ${showTimeGrid && viewStyle === 'simple' ? '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
|
||||
Simple
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setViewStyle('grid');
|
||||
setShowTimeGrid(false);
|
||||
saveSetting('viewStyle', 'grid');
|
||||
saveSetting('showTimeGrid', false);
|
||||
setViewStyle('calendar');
|
||||
setShowTimeGrid(true);
|
||||
saveSetting('viewStyle', 'calendar');
|
||||
saveSetting('showTimeGrid', true);
|
||||
}}
|
||||
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'}`}
|
||||
className={`px-2 py-0.5 text-xs rounded transition-colors ${showTimeGrid && viewStyle === 'calendar' ? '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
|
||||
Calendar
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setViewStyle('list');
|
||||
if (!showTimeGrid) {
|
||||
setShowTimeGrid(false);
|
||||
saveSetting('viewStyle', 'list');
|
||||
}
|
||||
saveSetting('showTimeGrid', false);
|
||||
}}
|
||||
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'}`}
|
||||
>
|
||||
@ -1982,7 +1988,7 @@ export default function WeeklyView() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CENTER SECTION: Week/Year, Motto, Focus Mode */}
|
||||
{/* CENTER SECTION: Week/Year, Motto, Focus Mode - Reveal on Hover */}
|
||||
<div className="flex items-center justify-center gap-6 absolute left-1/2 transform -translate-x-1/2">
|
||||
{/* Week & Year */}
|
||||
<div className="text-lg font-bold whitespace-nowrap">
|
||||
@ -2036,28 +2042,26 @@ export default function WeeklyView() {
|
||||
</div>
|
||||
|
||||
{/* RIGHT SECTION: Navigation & Tools */}
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-3 transition-opacity duration-300 opacity-0 group-hover:opacity-100">
|
||||
<button
|
||||
onClick={() => {
|
||||
const newVal = !showNextTask;
|
||||
setShowNextTask(newVal);
|
||||
saveSetting('showNextTask', newVal);
|
||||
}}
|
||||
className={`weekly-btn-secondary ${showNextTask ? 'active' : ''}`}
|
||||
className={`weekly-btn-icon ${showNextTask ? 'active' : ''}`}
|
||||
title={showNextTask ? "Showing Next Task" : "Showing Motto"}
|
||||
>
|
||||
<Target size={14} className={showNextTask ? 'text-teal-500' : 'text-gray-400'} />
|
||||
Now DO THIS
|
||||
{showNextTask ? <Play size={18} className="text-teal-600" /> : <Target size={18} className="text-gray-400" />}
|
||||
</button>
|
||||
|
||||
{/* Focus Mode Toggle */}
|
||||
<button
|
||||
onClick={() => setShowFocusMode(true)}
|
||||
className="weekly-btn-secondary"
|
||||
className="weekly-btn-icon"
|
||||
title="Enter Focus Mode"
|
||||
>
|
||||
<Target size={14} />
|
||||
<span>Focus Mode</span>
|
||||
<Zap size={18} className="text-gray-600 hover:text-yellow-500 transition-colors" />
|
||||
</button>
|
||||
|
||||
{/* Day/Night Mode Switch */}
|
||||
@ -3675,8 +3679,8 @@ interface SettingsSidebarProps {
|
||||
setCellDuration: (duration: CellDuration) => void;
|
||||
weekStartDay: number;
|
||||
setWeekStartDay: (day: number) => void;
|
||||
viewStyle: 'grid' | 'list';
|
||||
setViewStyle: (style: 'grid' | 'list') => void;
|
||||
viewStyle: ViewStyle;
|
||||
setViewStyle: (style: ViewStyle) => void;
|
||||
showSomeday: boolean;
|
||||
setShowSomeday: (show: boolean) => void;
|
||||
showAllDay: boolean;
|
||||
@ -3933,7 +3937,7 @@ function SettingsSidebar({
|
||||
|
||||
if (data.user.showTimeGrid !== undefined) setShowTimeGrid(data.user.showTimeGrid);
|
||||
if (data.user.cellDuration) setCellDuration(data.user.cellDuration as CellDuration);
|
||||
if (data.user.viewStyle) setViewStyle(data.user.viewStyle as 'grid' | 'list');
|
||||
if (data.user.viewStyle) setViewStyle(data.user.viewStyle as ViewStyle);
|
||||
if (data.user.fontSize) setFontSize(data.user.fontSize as 'S' | 'M' | 'L');
|
||||
if (data.user.focusTimerDuration) setFocusTimerDuration(data.user.focusTimerDuration);
|
||||
if (data.user.showSchedule !== undefined) setShowSchedule(data.user.showSchedule);
|
||||
@ -4020,8 +4024,9 @@ function SettingsSidebar({
|
||||
showSchedule: showSchedule,
|
||||
// The following will be taken from profile if present,
|
||||
// ensuring edited state is saved.
|
||||
focusBreakDuration: profile.focusBreakDuration || focusBreakDuration,
|
||||
focusTimerDuration: profile.focusTimerDuration || focusTimerDuration,
|
||||
// Validate numeric fields to avoid NaN
|
||||
focusBreakDuration: !isNaN(Number(profile.focusBreakDuration)) ? Number(profile.focusBreakDuration) : (focusBreakDuration || 5),
|
||||
focusTimerDuration: !isNaN(Number(profile.focusTimerDuration)) ? Number(profile.focusTimerDuration) : (focusTimerDuration || 25),
|
||||
password: (passwords.new && passwords.new.trim() !== "") ? passwords.new : undefined
|
||||
})
|
||||
});
|
||||
@ -4088,6 +4093,7 @@ function SettingsSidebar({
|
||||
// Temporary success message
|
||||
setTimeout(() => setAccountMsg(''), 3000);
|
||||
} else {
|
||||
console.error('Failed to update profile:', data);
|
||||
setAccountMsg(data.error || 'Failed to update profile');
|
||||
if (data.details) console.error('Update profile details:', data.details);
|
||||
}
|
||||
@ -4264,11 +4270,12 @@ function SettingsSidebar({
|
||||
<label style={{ display: 'block', fontSize: '0.9rem', fontWeight: 600, marginBottom: '4px' }}>{t.viewStyle}</label>
|
||||
<select
|
||||
value={viewStyle}
|
||||
onChange={(e) => setViewStyle(e.target.value as 'grid' | 'list')}
|
||||
onChange={(e) => setViewStyle(e.target.value as ViewStyle)}
|
||||
className="weekly-input"
|
||||
style={{ width: '100%', padding: '8px', border: '1px solid #ddd', borderRadius: '4px' }}
|
||||
>
|
||||
<option value="grid">{t.gridView}</option>
|
||||
<option value="simple">{t.simpleView}</option>
|
||||
<option value="calendar">{t.calendarView}</option>
|
||||
<option value="list">{t.listView}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user