Add Copy as Markdown button for easy email sharing
- New copyMarkdown() function generates clean Markdown with verdict, key dates table, and calculation table - Uses clipboard API with textarea fallback for broad browser support - Button shows brief "Copied!" confirmation feedback - Green emerald button sits alongside existing Calendar and CSV exports Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
006e5e6690
commit
29ad845ce0
2 changed files with 94 additions and 1 deletions
|
|
@ -540,7 +540,11 @@
|
|||
|
||||
<div class="flex flex-wrap justify-between gap-2 mt-4 no-print">
|
||||
<button onclick="goToStep(3)" class="px-6 py-2.5 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 rounded-lg font-medium text-sm transition-colors">← Adjust Dates</button>
|
||||
<div class="flex gap-2">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button onclick="copyMarkdown()" id="btnCopyMd" class="px-5 py-2.5 bg-emerald-600 hover:bg-emerald-700 text-white rounded-lg font-medium text-sm transition-colors flex items-center gap-2">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/></svg>
|
||||
Copy as Markdown
|
||||
</button>
|
||||
<button onclick="exportICal()" class="px-5 py-2.5 bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-medium text-sm transition-colors flex items-center gap-2">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>
|
||||
Add to Calendar
|
||||
|
|
|
|||
89
script.js
89
script.js
|
|
@ -748,6 +748,95 @@ function csvEscape(str) {
|
|||
return str;
|
||||
}
|
||||
|
||||
// ---- Copy as Markdown ----
|
||||
function copyMarkdown() {
|
||||
if (!lastCalculationData) return;
|
||||
const data = lastCalculationData;
|
||||
const briefType = document.getElementById('briefType').selectedOptions[0]?.text || '';
|
||||
const lines = [];
|
||||
|
||||
// Header & summary
|
||||
lines.push(`## SLA Summary — ${data.briefName || 'Untitled'}`);
|
||||
lines.push('');
|
||||
lines.push(`**Brief Type:** ${briefType}`);
|
||||
lines.push(`**Kick-Off Date:** ${formatDateLong(data.kickOff)}`);
|
||||
lines.push(`**Required Go-Live:** ${data.goLive ? formatDateLong(data.goLive) : 'Not set'}`);
|
||||
lines.push(`**Planned End Date (SLA):** ${formatDateLong(data.projectEndDate)}`);
|
||||
lines.push(`**Suggested Go-Live (SLA):** ${formatDateLong(data.suggestedGoLive)}`);
|
||||
|
||||
if (data.canMeet === null) {
|
||||
lines.push('**Verdict:** N/A — No Go-Live date set');
|
||||
} else if (data.canMeet) {
|
||||
lines.push('**Verdict:** ✅ YES — Can meet the deadline');
|
||||
} else {
|
||||
const gap = businessDaysBetween(data.goLive, data.projectEndDate);
|
||||
lines.push(`**Verdict:** ❌ NO — Cannot meet deadline (${gap} business days over)`);
|
||||
}
|
||||
|
||||
// Key Dates table
|
||||
lines.push('');
|
||||
lines.push('### Key Dates');
|
||||
lines.push('');
|
||||
lines.push('| Stage | Active | Stage Kick-Off | WIP 1 To Approval | Receive Feedback By | Rounds | Stage Complete By |');
|
||||
lines.push('|---|---|---|---|---|---|---|');
|
||||
data.stages.forEach(s => {
|
||||
const active = s.active ? '✅ Y' : '⬜ N';
|
||||
const kickOff = s.active ? formatDate(s.kickOffDate) : 'n.a.';
|
||||
const wip = s.active ? (s.noFeedback ? 'n/r' : formatDate(s.wipCompleteDate)) : 'n.a.';
|
||||
const feedback = s.active ? (s.noFeedback ? 'n/r' : (s.feedbackByDate ? formatDate(s.feedbackByDate) : formatDate(s.wipCompleteDate))) : 'n.a.';
|
||||
const rounds = s.active ? (s.noFeedback ? 'n/r' : s.rounds) : 'n.a.';
|
||||
const complete = s.active ? formatDate(s.completeDate) : 'n.a.';
|
||||
lines.push(`| ${s.label} | ${active} | ${kickOff} | ${wip} | ${feedback} | ${rounds} | ${complete} |`);
|
||||
});
|
||||
|
||||
// Calculation of Days table
|
||||
lines.push('');
|
||||
lines.push('### Calculation of Days');
|
||||
lines.push('');
|
||||
lines.push('| Stage | Active | Handover | WIP 1 | Feedback | Revisions | Complete By |');
|
||||
lines.push('|---|---|---|---|---|---|---|');
|
||||
data.stages.forEach(s => {
|
||||
const active = s.active ? '✅ Y' : '⬜ N';
|
||||
const handover = s.active ? s.handover : 0;
|
||||
const wip = s.active ? s.wip : 0;
|
||||
const feedback = s.active ? (s.noFeedback ? 'n/r' : s.feedback) : 0;
|
||||
const revisions = s.active ? (s.noFeedback ? 'n/r' : s.revisions) : 0;
|
||||
const complete = formatDate(s.completeDate);
|
||||
lines.push(`| ${s.label} | ${active} | ${handover} | ${wip} | ${feedback} | ${revisions} | ${complete} |`);
|
||||
});
|
||||
|
||||
const markdown = lines.join('\n');
|
||||
|
||||
// Copy to clipboard with fallback
|
||||
const showCopied = () => {
|
||||
const btn = document.getElementById('btnCopyMd');
|
||||
const originalHTML = btn.innerHTML;
|
||||
btn.innerHTML = `<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg> Copied!`;
|
||||
setTimeout(() => { btn.innerHTML = originalHTML; }, 2000);
|
||||
};
|
||||
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(markdown).then(showCopied).catch(() => {
|
||||
fallbackCopy(markdown);
|
||||
showCopied();
|
||||
});
|
||||
} else {
|
||||
fallbackCopy(markdown);
|
||||
showCopied();
|
||||
}
|
||||
}
|
||||
|
||||
function fallbackCopy(text) {
|
||||
const ta = document.createElement('textarea');
|
||||
ta.value = text;
|
||||
ta.style.position = 'fixed';
|
||||
ta.style.left = '-9999px';
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(ta);
|
||||
}
|
||||
|
||||
// ---- iCal Export ----
|
||||
function exportICal() {
|
||||
if (!lastCalculationData) return;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue