fix: restore sync spinner, fix task hover menu, and add custom motivational quote sources
- Reverted RefreshCcw icon to original CSS loading spinner - Fixed GridTaskBlock overflow to restore task actions menu visibility - Implemented motivationalQuote state and fetcher with custom API source setting - Removed redundant Goal of the Week setting from panel
This commit is contained in:
parent
f9d578ddf1
commit
6fed420375
@ -180,7 +180,7 @@ export function GridTaskBlock({
|
||||
boxShadow: (isNotesOpen || isSubTasksOpen || isResizing) ? "0 1px 3px rgba(0,0,0,0.05)" : "none",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflow: isNotesOpen || isSubTasksOpen ? "visible" : "hidden",
|
||||
overflow: "visible",
|
||||
}}
|
||||
draggable={!editingTaskId && !isResizing}
|
||||
onDragStart={(e) => handleDragStart(e, task)}
|
||||
|
||||
@ -539,6 +539,7 @@ export default function WeeklyView() {
|
||||
todayHighlightColor?: string;
|
||||
pastDayColor?: string;
|
||||
goalFallbackType?: "quote" | "next_todo" | "default";
|
||||
quoteSourceUrl?: string;
|
||||
goalDefaultSentence?: string;
|
||||
goalFontFamily?: string;
|
||||
goalFontSize?: string;
|
||||
@ -607,7 +608,9 @@ export default function WeeklyView() {
|
||||
weekendColorSat: "#666666",
|
||||
weekendColorSun: "#dc2626",
|
||||
pastDayColor: "#a6a6a7",
|
||||
quoteSourceUrl: "https://recite.vercel.app/api/random",
|
||||
});
|
||||
const [motivationalQuote, setMotivationalQuote] = useState("");
|
||||
const [showSummary, setShowSummary] = useState(false);
|
||||
|
||||
const [isAddingSomedayList, setIsAddingSomedayList] = useState(false);
|
||||
@ -940,14 +943,43 @@ export default function WeeklyView() {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchMotivationalQuote = useCallback(async () => {
|
||||
if (profile.goalFallbackType !== "quote") return;
|
||||
try {
|
||||
const url = profile.quoteSourceUrl || "https://recite.vercel.app/api/random";
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error("Failed to fetch quote");
|
||||
const data = await res.json();
|
||||
|
||||
// Handle different JSON formats (array or object)
|
||||
let quoteText = "";
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
const item = data[0];
|
||||
quoteText = item.quote || item.text || item.content || (typeof item === 'string' ? item : "");
|
||||
if (item.author) quoteText += ` - ${item.author}`;
|
||||
} else if (data && typeof data === 'object') {
|
||||
quoteText = data.quote || data.text || data.content || "";
|
||||
if (data.author) quoteText += ` - ${data.author}`;
|
||||
} else if (typeof data === 'string') {
|
||||
quoteText = data;
|
||||
}
|
||||
|
||||
if (quoteText) setMotivationalQuote(quoteText);
|
||||
} catch (error) {
|
||||
console.error("Error fetching motivational quote:", error);
|
||||
setMotivationalQuote("Stay focused and productive.");
|
||||
}
|
||||
}, [profile.goalFallbackType, profile.quoteSourceUrl]);
|
||||
|
||||
// Fetch tasks on mount
|
||||
useEffect(() => {
|
||||
if (session) {
|
||||
fetchTasks();
|
||||
fetchConnections();
|
||||
fetchCalendarEvents();
|
||||
fetchMotivationalQuote();
|
||||
}
|
||||
}, [session]);
|
||||
}, [session, fetchMotivationalQuote]);
|
||||
|
||||
// Periodic pull-sync from Google Tasks (every 2 minutes)
|
||||
useEffect(() => {
|
||||
@ -1104,18 +1136,21 @@ export default function WeeklyView() {
|
||||
setGoal(newGoal);
|
||||
try {
|
||||
await fetch("/api/goal", {
|
||||
method: "PUT",
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
weekStart: goalDateKey,
|
||||
text: newGoal,
|
||||
goal: newGoal,
|
||||
scope: profile.goalScope || "week",
|
||||
}),
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Failed to save goal:", err);
|
||||
} catch (error) {
|
||||
console.error("Error saving goal:", error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleSomedayWheel = (e: React.WheelEvent) => {
|
||||
if (e.currentTarget) {
|
||||
e.currentTarget.scrollLeft += e.deltaY;
|
||||
@ -3277,10 +3312,10 @@ export default function WeeklyView() {
|
||||
{/* Week & Year */}
|
||||
<div className="whitespace-nowrap flex items-center gap-2">
|
||||
{(isLoading || isSyncing || syncStatus === "syncing") && (
|
||||
<RefreshCcw
|
||||
className="animate-spin text-blue-600 mr-2"
|
||||
size={18}
|
||||
/>
|
||||
<div
|
||||
className="animate-spin rounded-full h-4 w-4 border-2 border-gray-300 border-t-blue-600 mr-2"
|
||||
title="Syncing..."
|
||||
></div>
|
||||
)}
|
||||
<span style={{
|
||||
fontFamily: profile.cwFontFamily || "Inter",
|
||||
@ -3364,9 +3399,10 @@ export default function WeeklyView() {
|
||||
return a.order - b.order;
|
||||
});
|
||||
const nextTask = todaysTasks[0];
|
||||
return nextTask ? `Do this now: ${nextTask.title}` : goal;
|
||||
const nextTaskText = nextTask ? `Do this now: ${nextTask.title}` : (goal || (profile.goalFallbackType === "quote" ? motivationalQuote : goal));
|
||||
return nextTaskText;
|
||||
})()
|
||||
: goal}
|
||||
: (goal || (profile.goalFallbackType === "quote" ? motivationalQuote : goal))}
|
||||
</span>
|
||||
)}
|
||||
<span className="text-gray-300 mx-2">-</span>
|
||||
@ -4145,7 +4181,7 @@ export default function WeeklyView() {
|
||||
onEdit={() => setEditingTaskId(task.id)}
|
||||
onUpdate={(newTitle) => updateTask(task.id, newTitle)}
|
||||
onDelete={() => deleteTask(task.id)}
|
||||
onNotes={(notes) => updateTaskNotes(task.id, notes)}
|
||||
onNotes={() => setSelectedTaskForNotes(task)}
|
||||
onRollToggle={() => toggleTaskRolling(task.id)}
|
||||
onRecurrence={() =>
|
||||
setSelectedTaskForRecurrence(task)
|
||||
@ -6274,6 +6310,7 @@ function SettingsSidebar({
|
||||
todayHighlightColor?: string;
|
||||
pastDayColor?: string;
|
||||
goalFallbackType?: "quote" | "next_todo" | "default";
|
||||
quoteSourceUrl?: string;
|
||||
goalDefaultSentence?: string;
|
||||
goalFontFamily?: string;
|
||||
goalFontSize?: string;
|
||||
@ -6316,6 +6353,8 @@ function SettingsSidebar({
|
||||
hourLabelFormat: "short",
|
||||
showSubHourSlots: true,
|
||||
allDayPosition: "below",
|
||||
goalFallbackType: "quote",
|
||||
quoteSourceUrl: "https://recite.vercel.app/api/random",
|
||||
headlineFont: "Inter",
|
||||
headlineFontSize: "1.25rem",
|
||||
headlineFontWeight: "900",
|
||||
@ -8833,45 +8872,7 @@ function SettingsSidebar({
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "column", gap: "24px" }}
|
||||
>
|
||||
{/* Goal of the Week */}
|
||||
<div
|
||||
style={{
|
||||
background: "var(--weekly-settings-item-bg)",
|
||||
padding: "20px",
|
||||
borderRadius: "12px",
|
||||
border: "1px solid var(--weekly-border)",
|
||||
}}
|
||||
>
|
||||
<label
|
||||
style={{
|
||||
display: "block",
|
||||
fontSize: "0.9rem",
|
||||
fontWeight: 600,
|
||||
marginBottom: "8px",
|
||||
color: "var(--weekly-settings-title)",
|
||||
}}
|
||||
>
|
||||
{t.goalOfWeek}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={goal}
|
||||
onChange={(e) => setGoal(e.target.value)}
|
||||
onBlur={() => saveGoal(goal)}
|
||||
onKeyDown={(e) => e.key === "Enter" && e.currentTarget.blur()}
|
||||
className="weekly-input"
|
||||
placeholder={t.goalOfWeek}
|
||||
style={{
|
||||
width: "100%",
|
||||
padding: "12px",
|
||||
fontSize: "1rem",
|
||||
borderRadius: "6px",
|
||||
border: "1px solid var(--weekly-border)",
|
||||
background: "var(--weekly-bg)",
|
||||
color: "var(--weekly-settings-text)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/* Replaced Goal of the Week settings block */}
|
||||
{/* "Do This Now" Toggle */}
|
||||
<div
|
||||
style={{
|
||||
@ -9110,6 +9111,43 @@ function SettingsSidebar({
|
||||
<option value="default">Standardtext</option>
|
||||
</select>
|
||||
</div>
|
||||
{profile.goalFallbackType === "quote" && (
|
||||
<div style={{ marginTop: "12px" }}>
|
||||
<label
|
||||
style={{
|
||||
display: "block",
|
||||
fontSize: "0.9rem",
|
||||
marginBottom: "4px",
|
||||
color: "var(--weekly-settings-label)",
|
||||
}}
|
||||
>
|
||||
API-Datenquelle (URL)
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={profile.quoteSourceUrl || ""}
|
||||
onChange={(e) =>
|
||||
setProfile((p) => ({
|
||||
...p,
|
||||
quoteSourceUrl: e.target.value,
|
||||
}))
|
||||
}
|
||||
className="weekly-input"
|
||||
placeholder="https://recite.vercel.app/api/random"
|
||||
style={{
|
||||
width: "100%",
|
||||
padding: "10px",
|
||||
fontSize: "0.95rem",
|
||||
borderRadius: "6px",
|
||||
border: "1px solid var(--weekly-border)",
|
||||
background: "var(--weekly-bg)",
|
||||
}}
|
||||
/>
|
||||
<p style={{ fontSize: "0.75rem", color: "var(--weekly-text-light)", marginTop: "4px" }}>
|
||||
URL that returns a JSON list or object of quotes (e.g. {'[{"quote":"...","author":"..."}]'} or {'{"quote":"..."}'}).
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{profile.goalFallbackType === "default" && (
|
||||
<div style={{ marginTop: "12px" }}>
|
||||
<label
|
||||
|
||||
Loading…
Reference in New Issue
Block a user