rebuild: complete redesign — enterprise coaching ops platform

- Full redesign for Agile/DT/Enterprise coach personas
- Light McKinsey-style UI: white + navy accent, Inter font
- Marcus: Team Portfolio (14 teams, filters, sparklines, maturity)
- Marcus: Team Detail (maturity bar, session log, action tracker)
- Marcus: Session Capture (tagging toolbar, AI coaching questions, AI summary)
- Priya: Workshop List + Live Capture (Decision/Tension/Question tags)
- Priya: Workshop Summary (AI-generated, grouped by capture type)
- David: Portfolio Reports + CEO Report Generator (Gemini-powered)
- AI Assistant: 5 modes (Questions/Prep/Workshop/Summary/CEO Report)
- Playbooks: Agile/DT/Facilitation library with filters
- Persona switcher: Marcus / Priya / David
- Netlify function: /api/gemini (server-side API proxy)
- netlify.toml: functions dir + API route redirect
This commit is contained in:
wmj
2026-08-26 12:27:08 +08:00
parent 2f3e5540c8
commit d793c40d10
15 changed files with 2492 additions and 651 deletions
+55
View File
@@ -0,0 +1,55 @@
export default async (req, context) => {
if (req.method === 'OPTIONS') {
return new Response(null, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
},
});
}
try {
const { prompt, context: ctx, systemPrompt } = await req.json();
const apiKey = process.env.GEMINI_API_KEY || 'AIzaSyDZn7tv1D3n2zuns8uaXIqp_z1FZTeVyKI';
const system = systemPrompt || 'You are an expert agile and enterprise coaching assistant for Falah Consulting. Be concise, practical, and outcome-focused.';
const fullPrompt = ctx ? `Context: ${ctx}\n\n${prompt}` : prompt;
const geminiRes = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${apiKey}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
systemInstruction: { parts: [{ text: system }] },
contents: [{ role: 'user', parts: [{ text: fullPrompt }] }],
generationConfig: { temperature: 0.7, maxOutputTokens: 1024 },
}),
}
);
if (!geminiRes.ok) {
const err = await geminiRes.json();
throw new Error(err?.error?.message || `Gemini HTTP ${geminiRes.status}`);
}
const data = await geminiRes.json();
const text = data?.candidates?.[0]?.content?.parts?.[0]?.text ?? '';
return new Response(JSON.stringify({ text }), {
status: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
});
} catch (err) {
return new Response(JSON.stringify({ error: err.message }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
}
};
export const config = { path: '/api/gemini' };