feat: Morning brief system with calendar & 6am cron

This commit is contained in:
root
2026-02-16 11:44:55 +00:00
parent 067be4d5a2
commit 73194284f4
5 changed files with 498 additions and 0 deletions
+138
View File
@@ -0,0 +1,138 @@
"use client";
import { createContext, useContext, useState, useEffect, ReactNode } from "react";
import { MorningBrief, defaultBrief } from "./types";
interface MorningBriefStore {
briefs: MorningBrief[];
todayBrief: MorningBrief | null;
generateBrief: () => Promise<void>;
getBriefByDate: (date: string) => MorningBrief | undefined;
}
const MorningBriefContext = createContext<MorningBriefStore | null>(null);
const STORAGE_KEY = "sitemente:morning-briefs";
export function MorningBriefProvider({ children }: { children: ReactNode }) {
const [briefs, setBriefs] = useState<MorningBrief[]>([]);
const [todayBrief, setTodayBrief] = useState<MorningBrief | null>(null);
// Load from localStorage on mount
useEffect(() => {
if (typeof window === "undefined") return;
const saved = localStorage.getItem(STORAGE_KEY);
if (saved) {
try {
const parsed = JSON.parse(saved);
if (Array.isArray(parsed)) {
setBriefs(parsed);
// Find today's brief
const today = new Date().toISOString().split("T")[0];
const todayBrief = parsed.find((b: MorningBrief) => b.date === today);
if (todayBrief) setTodayBrief(todayBrief);
}
} catch {
// Use default
}
}
}, []);
// Save to localStorage on change
useEffect(() => {
if (typeof window === "undefined") return;
if (briefs.length > 0) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(briefs));
}
}, [briefs]);
const generateBrief = async () => {
const today = new Date().toISOString().split("T")[0];
const now = new Date().toISOString();
// Get Mission Control tasks from localStorage
let myTasks = { pending: [], inProgress: [], dueToday: [] };
try {
const mcTasks = localStorage.getItem("sitemente:mission-control");
if (mcTasks) {
const tasks = JSON.parse(mcTasks);
myTasks = {
pending: tasks.filter((t: any) => t.status === "todo").map((t: any) => t.title).slice(0, 5),
inProgress: tasks.filter((t: any) => t.status === "in_progress").map((t: any) => t.title).slice(0, 3),
dueToday: tasks.filter((t: any) => t.dueDate === today).map((t: any) => t.title),
};
}
} catch (e) {
console.error("Failed to load Mission Control tasks", e);
}
// Mock data - in production, these would be API calls
const newBrief: MorningBrief = {
id: `brief-${today}`,
date: today,
generatedAt: now,
weather: {
location: "Benalmádena, Málaga",
temperature: "18°C",
condition: "Partly Cloudy",
humidity: "65%",
wind: "12 km/h NW",
},
aiNews: {
items: [
"OpenAI announces GPT-5 roadmap with enhanced reasoning",
"Google DeepMind achieves breakthrough in protein folding",
"European AI Act enters enforcement phase",
"Nvidia reports record AI chip demand",
],
},
things3Tasks: {
today: [
"Review SiteMente pricing page",
"Call with potential restaurant client",
"Update HolaCompi roadmap",
],
overdue: [],
},
myTasks,
market: {
usIndex: "S&P 500: +0.3%",
crypto: "BTC: $67,500 (+1.2%)",
sentiment: "bullish",
topNews: [
"Fed signals potential rate cut in March",
"Bitcoin ETF inflows hit record high",
"Tech earnings exceed expectations",
],
events: [
"US Jobs Report (8:30 AM ET)",
"Crypto Treasury Meeting (2:00 PM ET)",
],
},
};
setBriefs((prev) => {
const filtered = prev.filter((b) => b.date !== today);
return [...filtered, newBrief];
});
setTodayBrief(newBrief);
};
const getBriefByDate = (date: string) => {
return briefs.find((b) => b.date === date);
};
return (
<MorningBriefContext.Provider value={{ briefs, todayBrief, generateBrief, getBriefByDate }}>
{children}
</MorningBriefContext.Provider>
);
}
export function useMorningBrief() {
const ctx = useContext(MorningBriefContext);
if (!ctx) {
throw new Error("useMorningBrief must be used within MorningBriefProvider");
}
return ctx;
}
+59
View File
@@ -0,0 +1,59 @@
// Morning Brief Types
export interface MorningBrief {
id: string;
date: string; // YYYY-MM-DD
generatedAt: string; // ISO timestamp
// Weather
weather?: {
location: string;
temperature: string;
condition: string;
humidity: string;
wind: string;
};
// AI News
aiNews: {
items: string[];
};
// Things 3 Tasks (placeholder - needs integration)
things3Tasks?: {
today: string[];
overdue: string[];
};
// My actionable tasks (from Mission Control)
myTasks: {
pending: string[];
inProgress: string[];
dueToday: string[];
};
// Market sentiment
market: {
usIndex: string;
crypto: string;
sentiment: 'bullish' | 'bearish' | 'neutral';
topNews: string[];
events: string[];
};
}
export const defaultBrief: MorningBrief = {
id: '',
date: '',
generatedAt: '',
aiNews: { items: [] },
things3Tasks: { today: [], overdue: [] },
myTasks: { pending: [], inProgress: [], dueToday: [] },
market: {
usIndex: '',
crypto: '',
sentiment: 'neutral',
topNews: [],
events: [],
}
};