feat: view style tabs at top of settings as per-view context
Move view style selector (Simple/Calendar/List/Kanban) to the top of the general settings tab. Settings below now automatically apply to the selected view. Replaces the previous per-view badge approach with a cleaner tab-based UI: switch to a view tab, adjust its settings — done. Per-view settings: hour format, sub-hour labels, weather, checkboxes. v1.50.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3eff24143c
commit
a26dc5efa6
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "my-weekly-todo-list",
|
"name": "my-weekly-todo-list",
|
||||||
"version": "1.49.1",
|
"version": "1.50.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": {
|
||||||
|
|||||||
@ -8514,10 +8514,7 @@ export default function WeeklyView() {
|
|||||||
onEndHourChange: (h: number) => { setEndHour(h); saveSetting("endHour", h); },
|
onEndHourChange: (h: number) => { setEndHour(h); saveSetting("endHour", h); },
|
||||||
}}
|
}}
|
||||||
perView={{
|
perView={{
|
||||||
isPerView: isPerView as any,
|
|
||||||
togglePerView: togglePerView as any,
|
|
||||||
saveViewSetting: saveViewSetting as any,
|
saveViewSetting: saveViewSetting as any,
|
||||||
viewLabel: viewStyle === "simple" ? (language === "de" ? "Einfach" : "Simple") : viewStyle === "calendar" ? (language === "de" ? "Kalender" : "Calendar") : viewStyle === "list" ? (language === "de" ? "Liste" : "List") : "Kanban",
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -10055,10 +10052,7 @@ interface SettingsSidebarProps {
|
|||||||
onEndHourChange: (h: number) => void;
|
onEndHourChange: (h: number) => void;
|
||||||
};
|
};
|
||||||
perView: {
|
perView: {
|
||||||
isPerView: (key: string) => boolean;
|
|
||||||
togglePerView: (key: string, globalVal: any) => void;
|
|
||||||
saveViewSetting: (key: string, value: any, perView: boolean) => void;
|
saveViewSetting: (key: string, value: any, perView: boolean) => void;
|
||||||
viewLabel: string;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Notes Sidebar Component
|
// Notes Sidebar Component
|
||||||
@ -10286,31 +10280,6 @@ function SettingsSidebar({
|
|||||||
mobileActions,
|
mobileActions,
|
||||||
perView,
|
perView,
|
||||||
}: SettingsSidebarProps) {
|
}: SettingsSidebarProps) {
|
||||||
// Per-view badge: shows which view a setting applies to, click to toggle
|
|
||||||
const PerViewBadge = ({ settingKey, globalVal }: { settingKey: string; globalVal: any }) => {
|
|
||||||
const isPV = perView.isPerView(settingKey);
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
onClick={() => perView.togglePerView(settingKey, globalVal)}
|
|
||||||
title={isPV ? (profile.language === "de" ? `Nur für ${perView.viewLabel} — klicken für alle Ansichten` : `Only for ${perView.viewLabel} — click for all views`) : (profile.language === "de" ? `Alle Ansichten — klicken für nur ${perView.viewLabel}` : `All views — click for ${perView.viewLabel} only`)}
|
|
||||||
style={{
|
|
||||||
fontSize: "0.55rem",
|
|
||||||
padding: "1px 5px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
border: isPV ? "1px solid #6366f1" : "1px solid #d1d5db",
|
|
||||||
background: isPV ? "#eef2ff" : "transparent",
|
|
||||||
color: isPV ? "#6366f1" : "#9ca3af",
|
|
||||||
cursor: "pointer",
|
|
||||||
fontWeight: 600,
|
|
||||||
whiteSpace: "nowrap" as const,
|
|
||||||
marginLeft: "4px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isPV ? perView.viewLabel : (profile.language === "de" ? "Alle" : "All")}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState<
|
const [activeTab, setActiveTab] = useState<
|
||||||
"calendar" | "general" | "account" | "styling" | "motivation" | "about" | "localisation"
|
"calendar" | "general" | "account" | "styling" | "motivation" | "about" | "localisation"
|
||||||
>(initialTab || "general");
|
>(initialTab || "general");
|
||||||
@ -10837,6 +10806,33 @@ function SettingsSidebar({
|
|||||||
<div
|
<div
|
||||||
style={{ display: "flex", flexDirection: "column", gap: "20px" }}
|
style={{ display: "flex", flexDirection: "column", gap: "20px" }}
|
||||||
>
|
>
|
||||||
|
{/* View Style Tabs — at top, acts as context for per-view settings */}
|
||||||
|
<div>
|
||||||
|
<label style={{ display: "block", fontSize: "0.9rem", fontWeight: 600, marginBottom: "8px" }}>
|
||||||
|
{t.viewStyle}
|
||||||
|
</label>
|
||||||
|
<div className="flex items-center gap-1 bg-gray-100 dark:bg-gray-800 rounded p-1" style={{ width: "fit-content" }}>
|
||||||
|
<button onClick={() => { setViewStyle("simple"); setShowTimeGrid(true); saveSetting("viewStyle", "simple"); saveSetting("showTimeGrid", true); }}
|
||||||
|
className={`px-4 py-2 text-sm rounded transition-colors ${showTimeGrid && viewStyle === "simple" ? "bg-white shadow-sm font-bold text-black dark:bg-gray-700 dark:text-white" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700"}`}>
|
||||||
|
{t.simpleView}
|
||||||
|
</button>
|
||||||
|
<button onClick={() => { setViewStyle("calendar"); setShowTimeGrid(true); saveSetting("viewStyle", "calendar"); saveSetting("showTimeGrid", true); }}
|
||||||
|
className={`px-4 py-2 text-sm rounded transition-colors ${showTimeGrid && viewStyle === "calendar" ? "bg-white shadow-sm font-bold text-black dark:bg-gray-700 dark:text-white" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700"}`}>
|
||||||
|
{t.calendarView}
|
||||||
|
</button>
|
||||||
|
<button onClick={() => { setViewStyle("list"); setShowTimeGrid(false); saveSetting("viewStyle", "list"); saveSetting("showTimeGrid", false); }}
|
||||||
|
className={`px-4 py-2 text-sm rounded transition-colors ${!showTimeGrid && viewStyle === "list" ? "bg-white shadow-sm font-bold text-black dark:bg-gray-700 dark:text-white" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700"}`}>
|
||||||
|
{t.listView}
|
||||||
|
</button>
|
||||||
|
<button onClick={() => { setViewStyle("kanban"); saveSetting("viewStyle", "kanban"); }}
|
||||||
|
className={`px-4 py-2 text-sm rounded transition-colors ${viewStyle === "kanban" ? "bg-white shadow-sm font-bold text-black dark:bg-gray-700 dark:text-white" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700"}`}>
|
||||||
|
{t.kanbanView}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p style={{ fontSize: "0.7rem", color: "#9ca3af", marginTop: "4px" }}>
|
||||||
|
{profile.language === "de" ? "Einstellungen unten gelten für diese Ansicht" : "Settings below apply to this view"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{ display: "flex", alignItems: "center", gap: "8px" }}
|
style={{ display: "flex", alignItems: "center", gap: "8px" }}
|
||||||
@ -10960,18 +10956,15 @@ function SettingsSidebar({
|
|||||||
checked={profile.showTaskCheckboxes || false}
|
checked={profile.showTaskCheckboxes || false}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setProfile({ ...profile, showTaskCheckboxes: e.target.checked });
|
setProfile({ ...profile, showTaskCheckboxes: e.target.checked });
|
||||||
if (perView.isPerView("showTaskCheckboxes")) {
|
perView.saveViewSetting("showTaskCheckboxes", e.target.checked, true);
|
||||||
perView.saveViewSetting("showTaskCheckboxes", e.target.checked, true);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
style={{ width: "16px", height: "16px" }}
|
style={{ width: "16px", height: "16px" }}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="showTaskCheckboxes"
|
htmlFor="showTaskCheckboxes"
|
||||||
style={{ fontSize: "0.9rem", fontWeight: 600, display: "flex", alignItems: "center" }}
|
style={{ fontSize: "0.9rem", fontWeight: 600 }}
|
||||||
>
|
>
|
||||||
{t.showTaskCheckboxes}
|
{t.showTaskCheckboxes}
|
||||||
<PerViewBadge settingKey="showTaskCheckboxes" globalVal={profile.showTaskCheckboxes} />
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -11180,26 +11173,20 @@ function SettingsSidebar({
|
|||||||
<div style={{ marginTop: "4px" }}>
|
<div style={{ marginTop: "4px" }}>
|
||||||
<label
|
<label
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "block",
|
||||||
alignItems: "center",
|
|
||||||
fontSize: "0.9rem",
|
fontSize: "0.9rem",
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
marginBottom: "4px",
|
marginBottom: "4px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t.hourLabelFormat}
|
{t.hourLabelFormat}
|
||||||
<PerViewBadge settingKey="hourLabelFormat" globalVal={hourLabelFormat} />
|
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={hourLabelFormat}
|
value={hourLabelFormat}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const fmt = e.target.value as "short" | "full";
|
const fmt = e.target.value as "short" | "full";
|
||||||
setHourLabelFormat(fmt);
|
setHourLabelFormat(fmt);
|
||||||
if (perView.isPerView("hourLabelFormat")) {
|
perView.saveViewSetting("hourLabelFormat", fmt, true);
|
||||||
perView.saveViewSetting("hourLabelFormat", fmt, true);
|
|
||||||
} else {
|
|
||||||
saveSetting("hourLabelFormat", fmt);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
className="weekly-input"
|
className="weekly-input"
|
||||||
style={{
|
style={{
|
||||||
@ -11224,20 +11211,15 @@ function SettingsSidebar({
|
|||||||
checked={showSubHourSlots}
|
checked={showSubHourSlots}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setShowSubHourSlots(e.target.checked);
|
setShowSubHourSlots(e.target.checked);
|
||||||
if (perView.isPerView("showSubHourSlots")) {
|
perView.saveViewSetting("showSubHourSlots", e.target.checked, true);
|
||||||
perView.saveViewSetting("showSubHourSlots", e.target.checked, true);
|
|
||||||
} else {
|
|
||||||
saveSetting("showSubHourSlots", e.target.checked);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
style={{ width: "16px", height: "16px" }}
|
style={{ width: "16px", height: "16px" }}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="showSubHourSlots"
|
htmlFor="showSubHourSlots"
|
||||||
style={{ fontSize: "0.9rem", fontWeight: 600, display: "flex", alignItems: "center" }}
|
style={{ fontSize: "0.9rem", fontWeight: 600 }}
|
||||||
>
|
>
|
||||||
{t.showSubhourLabels}
|
{t.showSubhourLabels}
|
||||||
<PerViewBadge settingKey="showSubHourSlots" globalVal={showSubHourSlots} />
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -11246,66 +11228,6 @@ function SettingsSidebar({
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div style={{ marginTop: "16px" }}>
|
|
||||||
<label
|
|
||||||
style={{
|
|
||||||
display: "block",
|
|
||||||
fontSize: "0.9rem",
|
|
||||||
fontWeight: 600,
|
|
||||||
marginBottom: "8px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t.viewStyle}
|
|
||||||
</label>
|
|
||||||
<div
|
|
||||||
className="flex items-center gap-1 bg-gray-100 dark:bg-gray-800 rounded p-1"
|
|
||||||
style={{ width: "fit-content" }}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
setViewStyle("simple");
|
|
||||||
setShowTimeGrid(true);
|
|
||||||
saveSetting("viewStyle", "simple");
|
|
||||||
saveSetting("showTimeGrid", true);
|
|
||||||
}}
|
|
||||||
className={`px-4 py-2 text-sm 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"}`}
|
|
||||||
>
|
|
||||||
{t.simpleView}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
setViewStyle("calendar");
|
|
||||||
setShowTimeGrid(true);
|
|
||||||
saveSetting("viewStyle", "calendar");
|
|
||||||
saveSetting("showTimeGrid", true);
|
|
||||||
}}
|
|
||||||
className={`px-4 py-2 text-sm 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"}`}
|
|
||||||
>
|
|
||||||
{t.calendarView}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
setViewStyle("list");
|
|
||||||
setShowTimeGrid(false);
|
|
||||||
saveSetting("viewStyle", "list");
|
|
||||||
saveSetting("showTimeGrid", false);
|
|
||||||
}}
|
|
||||||
className={`px-4 py-2 text-sm 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"}`}
|
|
||||||
>
|
|
||||||
{t.listView}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
setViewStyle("kanban");
|
|
||||||
saveSetting("viewStyle", "kanban");
|
|
||||||
}}
|
|
||||||
className={`px-4 py-2 text-sm rounded transition-colors ${viewStyle === "kanban" ? "bg-white shadow-sm font-bold text-black" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700"}`}
|
|
||||||
>
|
|
||||||
{t.kanbanView}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Header Display */}
|
{/* Header Display */}
|
||||||
<div style={{ marginTop: "16px" }}>
|
<div style={{ marginTop: "16px" }}>
|
||||||
<label style={{ fontSize: "0.85rem", fontWeight: 600, color: "var(--weekly-settings-label)" }}>
|
<label style={{ fontSize: "0.85rem", fontWeight: 600, color: "var(--weekly-settings-label)" }}>
|
||||||
@ -11368,17 +11290,12 @@ function SettingsSidebar({
|
|||||||
checked={profile.weatherEnabled || false}
|
checked={profile.weatherEnabled || false}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setProfile({ ...profile, weatherEnabled: e.target.checked });
|
setProfile({ ...profile, weatherEnabled: e.target.checked });
|
||||||
if (perView.isPerView("weatherEnabled")) {
|
perView.saveViewSetting("weatherEnabled", e.target.checked, true);
|
||||||
perView.saveViewSetting("weatherEnabled", e.target.checked, true);
|
|
||||||
} else {
|
|
||||||
saveSetting("weatherEnabled", e.target.checked);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
style={{ width: "18px", height: "18px" }}
|
style={{ width: "18px", height: "18px" }}
|
||||||
/>
|
/>
|
||||||
<span style={{ fontSize: "0.85rem", fontWeight: 500, display: "flex", alignItems: "center" }}>
|
<span style={{ fontSize: "0.85rem", fontWeight: 500 }}>
|
||||||
{profile.language === "de" ? "Wetter anzeigen" : "Show weather"}
|
{profile.language === "de" ? "Wetter anzeigen" : "Show weather"}
|
||||||
<PerViewBadge settingKey="weatherEnabled" globalVal={profile.weatherEnabled} />
|
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
{profile.weatherEnabled && (
|
{profile.weatherEnabled && (
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user