diff --git a/components/SummaryTemplate.tsx b/components/SummaryTemplate.tsx index ec604ab..03b48a7 100644 --- a/components/SummaryTemplate.tsx +++ b/components/SummaryTemplate.tsx @@ -9,6 +9,7 @@ import { saveCustomAgenda } from '../utils/saveCustomAgenda'; import { generateMarkdown } from '../utils/generateMarkdown'; import axios from "axios"; import { filterFormData } from '../utils/filterFormData'; +import { getQuarterOptions, generateQuarterlyReport } from '../utils/quarterlyReportGenerator'; type SummaryTemplateProps = { updateMeetings: (newMeetingSummary: any) => void; @@ -71,6 +72,9 @@ const SummaryTemplate = ({ updateMeetings }: SummaryTemplateProps) => { const { myVariable, setMyVariable } = useMyVariable(); const [creatingDoc, setCreatingDoc] = useState(false); const today = new Date().toISOString().split('T')[0]; + const [selectedQuarter, setSelectedQuarter] = useState(''); + const [creatingQuarterlyDoc, setCreatingQuarterlyDoc] = useState(false); + const quarterOptions = getQuarterOptions(); const defaultFormData = { workgroup: "", @@ -120,11 +124,38 @@ const SummaryTemplate = ({ updateMeetings }: SummaryTemplateProps) => { const [tags, setTags] = useState({ topicsCovered: "", emotions: "", other: "", gamesPlayed: "" }); const currentOrder = myVariable.agendaItemOrder ? myVariable.agendaItemOrder[myVariable.workgroup?.workgroup] : undefined; + async function handleCreateQuarterlyDoc() { + setCreatingQuarterlyDoc(true); + + try { + const [quarter, year] = selectedQuarter.split(' '); + const quarterNumber = parseInt(quarter.slice(1)); + const markdown = await generateQuarterlyReport(myVariable.workgroup.workgroup_id, parseInt(year), quarterNumber, currentOrder); + //console.log(markdown); + const response = await axios.post('/api/createGoogleDoc', { + markdown, + workgroup: myVariable.workgroup.workgroup, + date: selectedQuarter + }); + + window.open(response.data.link, '_blank'); + } catch (error) { + console.error('Error creating Quarterly Google Doc:', error); + alert('There was an error creating the Quarterly Google Doc.'); + } finally { + setCreatingQuarterlyDoc(false); + } + } + async function handleCreateGoogleDoc() { setCreatingDoc(true); // Set creatingDoc to true when the button is clicked try { - const markdown = generateMarkdown(myVariable.summary, currentOrder); + let markdown = generateMarkdown(myVariable.summary, currentOrder); + + // Add a heading to the first line of the markdown + const heading = `# Meeting Summary for ${formData.workgroup}\nDate: ${formatTimestampForPdf(formData.meetingInfo?.date)}`; + markdown = `${heading}\n\n${markdown}`; //console.log("markdown", markdown); const response = await axios.post('/api/createGoogleDoc', { markdown, @@ -301,6 +332,32 @@ const SummaryTemplate = ({ updateMeetings }: SummaryTemplateProps) => {

(The document will open in a new tab if popups are enabled for this site)

+
+ + +
)} diff --git a/pages/api/createGoogleDoc.js b/pages/api/createGoogleDoc.js index 28974fb..7e7737d 100644 --- a/pages/api/createGoogleDoc.js +++ b/pages/api/createGoogleDoc.js @@ -3,6 +3,26 @@ import { google } from 'googleapis'; import { JWT } from 'google-auth-library'; import { parseMarkdown } from '../../utils/markdownToGoogleDocs'; +async function createFolderIfNotExists(drive, name, parentId) { + const query = `name='${name}' and mimeType='application/vnd.google-apps.folder' and '${parentId}' in parents`; + const response = await drive.files.list({ q: query, fields: 'files(id, name)' }); + + if (response.data.files.length > 0) { + return response.data.files[0].id; + } else { + const fileMetadata = { + name: name, + mimeType: 'application/vnd.google-apps.folder', + parents: [parentId] + }; + const folder = await drive.files.create({ + resource: fileMetadata, + fields: 'id' + }); + return folder.data.id; + } +} + export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ message: 'Method not allowed' }); @@ -22,12 +42,31 @@ export default async function handler(req, res) { const drive = google.drive({ version: 'v3', auth: jwtClient }); const docs = google.docs({ version: 'v1', auth: jwtClient }); - const folderId = process.env.GOOGLE_DRIVE_FOLDER_ID; + const baseFolderId = process.env.GOOGLE_DRIVE_FOLDER_ID; + + // Create folder structure + let year, month, parentFolderId; + + if (date.includes('Q')) { + // Handle quarterly date format (e.g., "Q3 2024") + year = date.split(' ')[1]; + const workgroupFolderId = await createFolderIfNotExists(drive, workgroup, baseFolderId); + parentFolderId = await createFolderIfNotExists(drive, year, workgroupFolderId); + } else { + // Handle regular date format + const dateObj = new Date(date); + year = dateObj.getFullYear().toString(); + month = (dateObj.getMonth() + 1).toString().padStart(2, '0'); + + const workgroupFolderId = await createFolderIfNotExists(drive, workgroup, baseFolderId); + const yearFolderId = await createFolderIfNotExists(drive, year, workgroupFolderId); + parentFolderId = await createFolderIfNotExists(drive, month, yearFolderId); + } const fileMetadata = { name: `Meeting Summary - ${workgroup} - ${date}`, mimeType: 'application/vnd.google-apps.document', - parents: [folderId] + parents: [parentFolderId] }; const file = await drive.files.create({ resource: fileMetadata, diff --git a/styles/summarytemplate.module.css b/styles/summarytemplate.module.css index b689eba..7e59d60 100644 --- a/styles/summarytemplate.module.css +++ b/styles/summarytemplate.module.css @@ -72,4 +72,31 @@ margin-top: 0.5rem; font-size: 0.9rem; color: #555; +} + +.quarterly-report-section { + margin-top: 2rem; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.quarterSelect { + padding: 0.5rem; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 1rem; + width: 100%; + background-color: white; + cursor: pointer; +} + +.quarterly-report-section .exportButton { + margin-top: 0; +} + +.quarterly-report-section .exportButton:disabled { + background-color: #e0e0e0; + color: #a0a0a0; + cursor: not-allowed; } \ No newline at end of file diff --git a/utils/markdownToGoogleDocs.js b/utils/markdownToGoogleDocs.js index 1a17f56..eb83f68 100644 --- a/utils/markdownToGoogleDocs.js +++ b/utils/markdownToGoogleDocs.js @@ -2,30 +2,6 @@ export function parseMarkdown(markdown, workgroup, date) { const requests = []; let currentIndex = 1; - // Add header text - const headerText = `Meeting Summary for ${workgroup}\nDate: ${date}\n\n`; - requests.push({ - insertText: { - location: { index: currentIndex }, - text: headerText, - }, - }); - currentIndex += headerText.length; - - // Apply heading style to the title - requests.push({ - updateParagraphStyle: { - range: { - startIndex: 1, - endIndex: headerText.indexOf('\n') + 1, - }, - paragraphStyle: { - namedStyleType: 'HEADING_1', - }, - fields: 'namedStyleType', - }, - }); - const lines = markdown.split('\n'); let listLevel = 0; // Keeping this for possible indentation handling diff --git a/utils/quarterlyReportGenerator.js b/utils/quarterlyReportGenerator.js new file mode 100644 index 0000000..1356b72 --- /dev/null +++ b/utils/quarterlyReportGenerator.js @@ -0,0 +1,80 @@ +// utils/quarterlyReportGenerator.js +import { supabase } from "../lib/supabaseClient"; +import { generateMarkdown } from './generateMarkdown'; + +function formatTimestampForPdf(timestamp) { + // Create a Date object using the timestamp + const date = new Date(timestamp); + + date.setHours(date.getHours() + 2); + + const day = date.getUTCDate(); + const month = date.toLocaleString('en-US', { month: 'long', timeZone: 'UTC' }); + const year = date.getUTCFullYear(); + + return `${day} ${month} ${year}`; + } + +export async function generateQuarterlyReport(workgroup_id, year, quarter, currentOrder) { + try { + // Define the start and end dates for the quarter + const startDate = new Date(Date.UTC(year, (quarter - 1) * 3, 1)); + const endDate = new Date(Date.UTC(year, quarter * 3, 0, 23, 59, 59, 999)); + + //console.log('Fetching summaries for:', workgroup_id, 'Quarter:', quarter, 'Year:', year); + //console.log('Date range:', startDate.toISOString(), 'to', endDate.toISOString()); + + // Fetch all confirmed summaries for the workgroup + const { data: allSummaries, error } = await supabase + .from('meetingsummaries') + .select('*') + .eq('workgroup_id', workgroup_id) + .eq('confirmed', true) + .order('date', { ascending: true }); + + if (error) { + console.error('Error fetching summaries:', error); + throw error; + } + + //console.log('Fetched all summaries:', allSummaries.length); + + // Filter summaries for the specific quarter + const summaries = allSummaries.filter(summary => { + const summaryDate = new Date(summary.date); + return summaryDate >= startDate && summaryDate <= endDate; + }); + + //console.log('Filtered summaries for the quarter:', summaries); + + if (summaries.length === 0) { + return `# No confirmed summaries found for Q${quarter} ${year}\n\nThere were no confirmed meeting summaries for this quarter.`; + } + + // Generate markdown for each summary + let markdownContent = `# Quarterly Summaries for ${summaries[0]?.summary.workgroup || 'Workgroup'} - Q${quarter} ${year}\n\n`; + + for (const summary of summaries) { + markdownContent += `## Summary for ${formatTimestampForPdf(summary.date)}\n\n`; + markdownContent += generateMarkdown(summary.summary, currentOrder) + '\n\n'; + } + + return markdownContent; + } catch (error) { + console.error('Error in generateQuarterlyReport:', error); + throw error; + } +} + +export function getQuarterOptions() { + const currentYear = new Date().getFullYear(); + const options = []; + + for (let year = currentYear; year >= currentYear - 2; year--) { + for (let quarter = 4; quarter >= 1; quarter--) { + options.push(`Q${quarter} ${year}`); + } + } + + return options; +} \ No newline at end of file