Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components/ArchiveSummaries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const ArchiveSummaries = () => {

useEffect(() => {
setRenderedMarkdown(formData.meetingSummary);
console.log(formData.meetingSummary)
}, [formData.meetingSummary]);

useEffect(() => {
Expand Down
2 changes: 1 addition & 1 deletion components/SummaryAgendaItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ const getHeading = (itemType: any, workgroup: any) => {
isEnabled: (template: any) => template?.townHallSummary === 1,
render: (item: any, agendaIndex: any) => (
<>
<h3>Town Hall Summary</h3>
<h3>Town Hall Summary (Optional)</h3>
<div className={styles['action-item']}>
<Item
type="townHallSummary"
Expand Down
4 changes: 2 additions & 2 deletions components/SummaryMeetingInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ const SummaryMeetingInfo: React.FC<SummaryMeetingInfoProps> = ({ workgroup, onUp
transcriptLink = '',
mediaLink = '',
workingDocs = [{ title: '', link: '' }],
timestampedVideo = { url: '', intro: '', timestamps: [{ title: '', timestamp: '' }] }
timestampedVideo = { url: '', intro: '', timestamps: '' }
} = myVariable?.summary?.meetingInfo || {};

// Set the local meetingInfo state with the values from myVariable.summary.meetingInfo
Expand Down Expand Up @@ -354,7 +354,7 @@ const SummaryMeetingInfo: React.FC<SummaryMeetingInfoProps> = ({ workgroup, onUp
)}
{myVariable.workgroup?.preferred_template?.meetingInfo?.googleSlides == 1 && (<>
<label className={styles['form-label']}>
Google Slides (Will be embedded in GitBook):
Google Slides (Optional):
</label>
<input
type="text"
Expand Down
12 changes: 12 additions & 0 deletions components/SummaryTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,18 @@ const SummaryTemplate = ({ updateMeetings }: SummaryTemplateProps) => {
const currentOrder = myVariable.agendaItemOrder ? myVariable.agendaItemOrder[myVariable.workgroup?.workgroup] : undefined;

async function generatePdf(markdown: any) {
const embedRegex = /\{% embed url="([^"]+)" %\}/g;
const updatedMarkdown = markdown.replace(embedRegex, (match, url) => {
// Check if the URL is a YouTube video
if (url.includes("youtube.com") || url.includes("youtu.be")) {
return `[Watch Video](${url})`; // Replace with a descriptive text for videos
} else if (url.includes("google.slides.com") || url.includes("docs.google.com/presentation")) {
return `[View Slides](${url})`; // Replace with a descriptive text for slides
} else {
return `[Link](${url})`; // A generic replacement for other URLs
}
});
markdown = updatedMarkdown;
try {
//console.log(formData.meetingInfo?.date, myVariable.summary?.date)
const additionalLines = `# Meeting Summary for ${myVariable.workgroup?.workgroup}\n` +
Expand Down
93 changes: 27 additions & 66 deletions components/TimestampedVideo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,26 @@ import { useState, useEffect } from 'react';
import styles from '../styles/timestampedVideo.module.css';

type TimestampedVideoProps = {
onUpdate: (videoData: any) => void;
initialData?: {
url: string;
intro: string;
timestamps: { title: string; timestamp: string }[];
};
onUpdate: (videoData: any) => void;
initialData?: {
url: string;
intro: string;
timestamps: string;
};
};

const TimestampedVideo: React.FC<TimestampedVideoProps> = ({ onUpdate, initialData }) => {
const [videoData, setVideoData] = useState({
url: initialData?.url || '',
intro: initialData?.intro || '',
timestamps: initialData?.timestamps || [{ title: '', timestamp: '' }],
timestamps: initialData?.timestamps || '',
});

const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, index: number | null) => {
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
if (index !== null) {
// Handle change for timestamps
const updatedTimestamps = [...videoData.timestamps];
updatedTimestamps[index] = { ...updatedTimestamps[index], [name]: value };
setVideoData({ ...videoData, timestamps: updatedTimestamps });
} else {
// Handle change for URL and Intro
setVideoData({ ...videoData, [name]: value });
}
};

const addTimestamp = () => {
setVideoData({
...videoData,
timestamps: [...videoData.timestamps, { title: '', timestamp: '' }],
});
setVideoData({ ...videoData, [name]: value });
};

const removeTimestamp = (index: number) => {
const filteredTimestamps = videoData.timestamps.filter((_, i) => i !== index);
setVideoData({ ...videoData, timestamps: filteredTimestamps });
};

// useEffect hook to call onUpdate whenever videoData changes
useEffect(() => {
onUpdate(videoData); // Call onUpdate with the current state of videoData
}, [videoData, onUpdate]); // Add videoData and onUpdate to the dependency array
Expand All @@ -57,45 +35,28 @@ const TimestampedVideo: React.FC<TimestampedVideoProps> = ({ onUpdate, initialDa
type="text"
name="url"
value={videoData.url}
onChange={(e) => handleChange(e, null)}
onChange={handleChange}
/>
</div>
<div className={styles.field}>
<label className={styles.label}>Video description (Optional):</label>
<textarea
className={styles.textarea}
name="intro"
value={videoData.intro}
onChange={handleChange}
/>
</div>
<div className={styles.field}>
<label className={styles.label}>Video description:</label>
<div className={styles.field}>
<label className={styles.label}>Timestamps:</label>
<textarea
className={styles.textarea}
name="intro"
value={videoData.intro}
onChange={(e) => handleChange(e, null)}
className={styles.textarea}
name="timestamps"
value={videoData.timestamps}
onChange={handleChange}
placeholder="Paste timestamps here"
/>
</div>
<label className={styles.label}>Timestamps:</label>
{videoData.timestamps.map((item, index) => (
<div key={index} className={styles.timestampField}>
<input
className={styles.input}
type="text"
name="title"
placeholder="Title"
value={item.title}
onChange={(e) => handleChange(e, index)}
/>
<input
className={styles.input}
type="text"
name="timestamp"
placeholder="hh:mm:ss"
value={item.timestamp}
onChange={(e) => handleChange(e, index)}
pattern="(?:\d{1,2}:)?[0-5]?\d:[0-5]\d|\d{1,2}"
title="Timestamp format: ss or mm:ss or hh:mm:ss"
/>
<button className={styles.removeButton} type="button" onClick={() => removeTimestamp(index)}>
Remove
</button>
</div>
))}
<button className={styles.button} type="button" onClick={addTimestamp}>Add Timestamp</button>
</div>
</div>
);
};
Expand Down
3 changes: 2 additions & 1 deletion pages/submit-meeting-summary/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,10 @@ useEffect(() => {
title="Defaults to latest meeting, only change this when you want to use a previous meeting as template">
{meetings.map((meeting: any) => (
<option
style={{ color: meeting.confirmed ? 'lightgreen' : 'white' }}
key={`${meeting.meeting_id}-${meeting.updated_at}`}
value={meeting.meeting_id}>
{formatDate(meeting.date)} {meeting.username}
{formatDate(meeting.date)} {meeting.username} {meeting.confirmed ? 'Archived' : ''}
</option>
))}
</select>
Expand Down
66 changes: 34 additions & 32 deletions utils/generateMarkdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@
* - Rich markdown formatting for clear and readable output.
*/

function capitalize(text) {
return text
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join(' ');
}

export function generateMarkdown(summary, order) {

let markdown = "";
Expand All @@ -42,11 +49,11 @@ export function generateMarkdown(summary, order) {
}
if (purpose) markdown += `- Purpose: ${purpose}\n`;
if (townHallNumber) markdown += `- Town Hall Number: ${townHallNumber}\n`; //townHallNumber
if (meetingVideoLink) markdown += `- Meeting video: ${meetingVideoLink}\n`;
if (mediaLink) markdown += `- Media link: ${mediaLink}\n`;
if (miroBoardLink) markdown += `- Miro board: ${miroBoardLink}\n`;
if (transcriptLink) markdown += `- Transcript: ${transcriptLink}\n`;
if (otherMediaLink) markdown += `- Other media: ${otherMediaLink}\n`;
if (meetingVideoLink) markdown += `- Meeting video: [Link](${meetingVideoLink})\n`;
if (mediaLink) markdown += `- Media link: [Link](${mediaLink})\n`;
if (miroBoardLink) markdown += `- Miro board: [Link](${miroBoardLink})\n`;
if (transcriptLink) markdown += `- Transcript: [Link](${transcriptLink})\n`;
if (otherMediaLink) markdown += `- Other media: [Link](${otherMediaLink})\n`;
//markdown += '\n';

// Process workingDocs
Expand All @@ -60,37 +67,32 @@ export function generateMarkdown(summary, order) {
markdown += `\n`
}

if (timestampedVideo && Array.isArray(timestampedVideo.timestamps) && timestampedVideo.timestamps.length > 0) {
const { url, intro, timestamps } = timestampedVideo;
// Process timestampedVideo if it's a string
if (timestampedVideo && typeof timestampedVideo.timestamps === 'string') {
const { url, intro } = timestampedVideo;
markdown += `\n#### Timestamped video:\n`;
// Embed video URL
markdown += `{% embed url="${url}" %}\n\n`;

// Add intro text
if (intro) {
markdown += `${intro}\n\n`;
}

// Add timestamps
timestamps.forEach(({ title, timestamp }) => {
// Split timestamp and reverse to process seconds, minutes, and optionally hours
const parts = timestamp.split(':').reverse().map(t => parseInt(t, 10));
let totalSeconds = 0;

// Calculate total seconds based on the presence of hours, minutes, and seconds
if (parts.length === 3) { // hh:mm:ss format
totalSeconds = parts[0] + parts[1] * 60 + parts[2] * 3600;
} else if (parts.length === 2) { // mm:ss format
totalSeconds = parts[0] + parts[1] * 60;
} else if (parts.length === 1) { // ss format
totalSeconds = parts[0];
if (url) markdown += `{% embed url="${url}" %}\n\n`;
if (intro) markdown += `${intro}\n\n`;

// Parse the timestamps string and convert each to a link
const lines = timestampedVideo.timestamps.split('\n');
lines.forEach(line => {
const [time, title] = line.split(/(?<=^\S+)\s/); // Splits at the first space after a non-whitespace sequence
if (time && title) {
const parts = time.split(':').map(t => parseInt(t, 10));
let totalSeconds = 0;
if (parts.length === 3) {
totalSeconds = parts[2] + parts[1] * 60 + parts[0] * 3600;
} else if (parts.length === 2) {
totalSeconds = parts[1] + parts[0] * 60;
} else if (parts.length === 1) {
totalSeconds = parts[0];
}
markdown += `[${time}](${url}\\&t=${totalSeconds}s) ${capitalize(title)}\n`;
}

markdown += `[${timestamp}](${url}\\&t=${totalSeconds}s) ${title}\n`;
});

markdown += `\n`;
}
}

if (googleSlides) {
markdown += `\n#### Slides:\n`;
Expand Down
9 changes: 6 additions & 3 deletions utils/sendDiscordMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,13 @@ export async function sendDiscordMessage(myVariable, markdown) {
const workgroup = myVariable.summary.workgroup;
const username = myVariable.summary.meetingInfo.documenter;
const archivist = myVariable.currentUser;
const date = myVariable.summary.meetingInfo.date;

const dateObj = new Date(myVariable.summary.meetingInfo.date);
// Format the date to "24 January 2024" format
const formattedDate = dateObj.toLocaleDateString('en-GB', {
day: 'numeric', month: 'long', year: 'numeric'
});
// Use the new function to create embeds
const title = `${workgroup} -> ${date} meeting summary`;
const title = `${workgroup} -> ${formattedDate} meeting summary`;
const footerText = `Summary created by ${username} and archived by ${archivist}`;
const embeds = createDiscordEmbeds(markdown, title, footerText);

Expand Down