feat: Morning brief system with calendar & 6am cron
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user