diff --git a/assets/css/main.css b/assets/css/main.css index 08a5c19..0575114 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -3,10 +3,12 @@ @import "tailwindcss"; @import "@nuxt/ui"; +/* Ubuntu font import */ +@import url("https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&family=Ubuntu+Mono:wght@400;700&display=swap"); + [data-theme="dark"] { html { @apply bg-white text-neutral-900; } html.dark { @apply bg-neutral-950 text-neutral-100; } - } /* Disable all animations, transitions, and smooth scrolling app-wide */ @@ -22,12 +24,347 @@ body { transition: none !important; } -.document-page { - @apply max-w-4xl mx-auto relative p-8 border-1 border-neutral-900 dark:border-neutral-100; +/* ========================= + TEMPLATE DOCUMENT LAYOUT + ========================= */ + +/* Template wrapper and document styling */ +.template-wrapper { + @apply min-h-screen relative; + } -/* Bitmap aesthetic overrides - remove all rounded corners */ +.document-page { + @apply max-w-full mx-auto relative p-8 border-1 border-neutral-900 dark:border-neutral-100; + +} + + + +/* ========================= + SECTION STYLING + ========================= */ + +.section-card { + @apply border border-neutral-200 dark:border-neutral-800 rounded-lg p-5 mb-8; +} + +.section-card::before { + content: ""; + position: absolute; + top: 4px; + left: 4px; + right: -4px; + bottom: -4px; + background: black; + background-image: radial-gradient(white 1px, transparent 1px); + background-size: 2px 2px; + z-index: -1; +} + +.section-title { + font-size: 1.75rem; + font-weight: 800; + color: inherit; + margin: 0 0 1rem 0; +} + +.subsection-title { + font-size: 1.25rem; + font-weight: 600; + color: #374151; + margin: 0 0 0.75rem 0; + text-decoration: none; + border-bottom: 1px solid #e5e7eb; + padding-bottom: 0.25rem; +} + +/* ========================= + FORM STYLING + ========================= */ + +.form-group-large { + margin-bottom: 1.5rem; + width: 100%; +} + +.form-group-large > * { + width: 100%; +} + +/* Ensure consistent alignment for all form fields */ +.form-group-large :deep(textarea), +.form-group-large :deep(input), +.form-group-large :deep(select), +.form-group-large :deep(.ui-select), +.form-group-large :deep(.ui-input), +.form-group-large :deep(.ui-textarea) { + margin-left: 0 !important; + padding-left: 0.75rem !important; +} + +/* Additional targeting for UInput, USelect, UTextarea components */ +.form-group-large :deep(.u-input), +.form-group-large :deep(.u-select), +.form-group-large :deep(.u-textarea), +.form-group-large :deep([data-headlessui-state]), +.form-group-large :deep(.relative) { + margin-left: 0 !important; +} + +.form-group-large :deep(.u-input input), +.form-group-large :deep(.u-select select), +.form-group-large :deep(.u-textarea textarea) { + margin-left: 0 !important; + padding-left: 0.75rem !important; +} + +.form-group-large .large-field { + @apply block w-full mt-2 text-lg rounded-md border-none transition-colors duration-150 ease-in-out; +} + +.form-group-large .large-field:focus { + background: #f3f4f6; + box-shadow: none; + outline: 2px solid #3b82f6; + outline-offset: -2px; +} + +.inline-field { + display: inline-block; + margin: 0 0.25rem; + min-width: 120px; + border: none; + background: #f9fafb; + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; +} + +.inline-field:focus { + background: #f3f4f6; + outline: 1px solid #3b82f6; + outline-offset: -1px; +} + +.number-field { + min-width: 80px !important; + text-align: center; +} + +.wide-field { + min-width: 250px !important; +} + +.form-group-block .block-field { + display: block; + width: 100%; + margin-top: 0.25rem; + border: none; + background: #f9fafb; + padding: 0.5rem; + border-radius: 0.25rem; +} + +.form-group-block .block-field:focus { + background: #f3f4f6; + outline: 1px solid #3b82f6; + outline-offset: -1px; +} + +/* ========================= + CONTENT STYLING + ========================= */ + +.content-paragraph { + margin-bottom: 0.75rem; + line-height: 1.6; + text-align: left; +} + +.content-list { + margin: 0.5rem 0; + padding-left: 1.5rem; +} + +.content-list li { + margin-bottom: 0.5rem; + line-height: 1.5; + display: list-item; + list-style-position: outside; +} + +.content-list.numbered { + list-style-type: decimal; + counter-reset: list-counter; +} + +.content-list.numbered li { + list-style-type: decimal; + display: list-item; +} + +.content-list:not(.numbered) { + list-style-type: disc; +} + +.content-list:not(.numbered) li { + list-style-type: disc; + display: list-item; +} + +/* Ensure bullets are visible even with flex items */ +.content-list li.flex { + display: list-item; +} + +.content-list li .flex { + display: flex; + width: 100%; +} + +/* Fix flex list items to show bullets */ +.content-list li[class*="flex"] { + display: list-item !important; + list-style-position: outside !important; +} + +/* Ensure numbered lists show numbers even with flex */ +.content-list.numbered li[class*="flex"] { + list-style-type: decimal !important; +} + +/* Ensure bullet lists show bullets even with flex */ +.content-list:not(.numbered) li[class*="flex"] { + list-style-type: disc !important; +} + +/* ========================= + CHECKBOX AND VALUES GRID + ========================= */ + +.checkbox-item { + @apply flex; +} + +.checkbox-group { + @apply flex flex-col space-y-3; +} + +.values-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 0.75rem; + margin-top: 10px; +} + +/* ========================= + DITHERED SHADOW EFFECTS + ========================= */ + +.dither-shadow { + background: black; + background-image: radial-gradient(white 1px, transparent 1px); + background-size: 2px 2px; +} + +html.dark .dither-shadow { + background: white; + background-image: radial-gradient(black 1px, transparent 1px); +} + +/* ========================= + BUTTON STYLING + ========================= */ + +.export-btn { + @apply bg-white border border-black text-black font-mono uppercase font-normal tracking-wide; + padding: 0.5rem 0.75rem; + font-size: 0.875rem; + font-weight: 500; + display: inline-flex; + align-items: center; + gap: 0.5rem; + cursor: pointer; + text-decoration: none; + min-width: fit-content; +} + +.export-btn:hover { + @apply bg-black text-white -translate-x-px -translate-y-px; +} + +.export-btn.primary { + @apply bg-black text-white; +} + +.export-btn.primary:hover { + @apply bg-black -translate-x-px -translate-y-px; +} + +.export-btn svg { + flex-shrink: 0; + width: 16px; + height: 16px; +} + +.export-btn.large svg { + width: 20px; + height: 20px; +} + +/* Dark mode export buttons */ +html.dark .export-btn { + @apply bg-neutral-950 border-white text-white; +} + +html.dark .export-btn:hover { + @apply bg-white text-black; +} + +html.dark .export-btn.primary { + @apply bg-white text-black; +} + +html.dark .export-btn.primary:hover { + @apply bg-white text-black; +} + +/* General buttons with bitmap styling */ +button:not(.export-btn) { + background: white !important; + border: 1px solid black !important; + color: black !important; + font-family: "Ubuntu Mono", monospace !important; + text-transform: uppercase !important; + font-weight: bold !important; + letter-spacing: 0.5px !important; + cursor: pointer !important; +} + +button:not(.export-btn):hover { + background: black !important; + color: white !important; + transform: translateY(-1px) translateX(-1px) !important; +} + +/* Dark mode buttons */ +html.dark button:not(.export-btn) { + background: #0a0a0a !important; + border: 1px solid white !important; + color: white !important; +} + +html.dark button:not(.export-btn):hover { + background: white !important; + color: black !important; +} + +/* ========================= + BITMAP AESTHETIC OVERRIDES + ========================= */ + +/* Remove all rounded corners */ * { border-radius: 0 !important; font-family: "Ubuntu", monospace !important; @@ -37,6 +374,188 @@ body { input, textarea, select { + border: 1px solid black !important; + background: white !important; + color: black !important; font-family: "Ubuntu Mono", monospace !important; } +input:focus, +textarea:focus, +select:focus { + outline: 2px solid black !important; + outline-offset: -2px !important; + background: white !important; +} + +/* Dark mode form fields */ +html.dark input, +html.dark textarea, +html.dark select { + border: 1px solid white !important; + background: #0a0a0a !important; + color: white !important; +} + +html.dark input:focus, +html.dark textarea:focus, +html.dark select:focus { + outline: 2px solid white !important; + background: #0a0a0a !important; +} + +/* Checkbox and radio button styling */ +input[type="checkbox"], +input[type="radio"] { + border: 2px solid black !important; + background: white !important; +} + +input[type="checkbox"]:checked, +input[type="radio"]:checked { + background: black !important; + color: white !important; +} + +html.dark input[type="checkbox"], +html.dark input[type="radio"] { + border: 2px solid white !important; + background: #0a0a0a !important; +} + +html.dark input[type="checkbox"]:checked, +html.dark input[type="radio"]:checked { + background: white !important; + color: black !important; +} + +/* Document titles */ +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: "Ubuntu", monospace !important; + color: inherit !important; +} + +/* All text */ +p, +span, +div { + color: inherit !important; + font-family: "Ubuntu", monospace !important; +} + +/* ========================= + HIDE ELEMENTS FROM PRINT + ========================= */ + +.no-print, +.no-pdf { + display: block; +} + +/* ========================= + PRINT STYLES + ========================= */ + +@media print { + .no-print, + .no-pdf { + display: none !important; + } + + .template-wrapper { + background: white !important; + padding: 0 !important; + min-height: auto !important; + } + + .document-page { + max-width: none !important; + width: 100% !important; + margin: 0 !important; + box-shadow: none !important; + border-radius: 0 !important; + padding: 0 !important; + } + + .document-page::before { + content: ""; + display: block; + height: 0.5in; + } + + .section-title { + font-size: 14pt; + page-break-after: avoid; + } + + .section-card { + break-inside: avoid; + margin-bottom: 1rem; + padding: 0.5rem; + border: 1px solid #ccc; + box-shadow: none; + } + + .checkbox-item { + font-size: 10pt; + margin: 2pt 0; + } + + .inline-field, + .large-field, + .block-field { + background: none !important; + border: none !important; + border-bottom: 1pt solid #000 !important; + padding: 2pt !important; + } + + .signature-space { + border: 1px solid #000; + background: none; + min-height: 3rem; + padding: 1rem; + } +} + +/* ========================= + MOBILE RESPONSIVENESS + ========================= */ + +@media (max-width: 768px) { + .template-wrapper { + padding: 1rem; + } + + .values-grid { + grid-template-columns: 1fr; + } + + .export-content { + flex-direction: column; + align-items: stretch; + gap: 1rem; + } + + /* Make principle cards full width on mobile */ + .principle-grid { + grid-template-columns: 1fr; + } + + /* Stack constraint buttons vertically on mobile */ + .constraint-buttons { + flex-direction: column; + align-items: stretch; + } + + .constraint-buttons button { + width: 100%; + justify-content: center; + } +} + diff --git a/components/ExportOptions.vue b/components/ExportOptions.vue index c2e83b4..b4860e9 100644 --- a/components/ExportOptions.vue +++ b/components/ExportOptions.vue @@ -4,32 +4,31 @@

Export Options:

- - - -
@@ -45,9 +44,9 @@ interface Props { } const props = withDefaults(defineProps(), { - filename: 'export', - title: 'Export Data', - containerClass: 'centered' + filename: "export", + title: "Export Data", + containerClass: "centered", }); const isProcessing = ref(false); @@ -58,53 +57,56 @@ const copyButton = ref(); const downloadButton = ref(); // Success feedback animation -const showSuccessFeedback = (buttonRef: Ref, successRef: Ref) => { +const showSuccessFeedback = ( + buttonRef: Ref, + successRef: Ref +) => { if (!buttonRef.value) return; - + successRef.value = true; - + // Add checkmark overlay animation const button = buttonRef.value; - button.classList.add('success-state'); - + button.classList.add("success-state"); + setTimeout(() => { successRef.value = false; - button.classList.remove('success-state'); + button.classList.remove("success-state"); }, 2000); }; const copyToClipboard = async () => { if (isProcessing.value) return; - + isProcessing.value = true; - + try { const textContent = extractTextContent(); - + if (navigator.clipboard && navigator.clipboard.writeText) { await navigator.clipboard.writeText(textContent); } else { // Fallback for browsers without clipboard API - const textarea = document.createElement('textarea'); + const textarea = document.createElement("textarea"); textarea.value = textContent; - textarea.style.position = 'fixed'; - textarea.style.opacity = '0'; + textarea.style.position = "fixed"; + textarea.style.opacity = "0"; document.body.appendChild(textarea); textarea.focus(); textarea.select(); - - const successful = document.execCommand('copy'); + + const successful = document.execCommand("copy"); document.body.removeChild(textarea); - + if (!successful) { - throw new Error('execCommand copy failed'); + throw new Error("execCommand copy failed"); } } - + showSuccessFeedback(copyButton, showCopySuccess); } catch (error) { - console.error('Copy failed:', error); - alert('Copy failed. Please try again or use the download option.'); + console.error("Copy failed:", error); + alert("Copy failed. Please try again or use the download option."); } finally { isProcessing.value = false; } @@ -112,71 +114,74 @@ const copyToClipboard = async () => { const downloadAsMarkdown = () => { if (isProcessing.value) return; - + isProcessing.value = true; - + try { const content = convertToMarkdown(); - downloadFile(content, `${props.filename}.md`, 'text/markdown'); + downloadFile(content, `${props.filename}.md`, "text/markdown"); showSuccessFeedback(downloadButton, showDownloadSuccess); } catch (error) { - console.error('Markdown download failed:', error); - alert('Download failed. Please try again.'); + console.error("Markdown download failed:", error); + alert("Download failed. Please try again."); } finally { isProcessing.value = false; } }; - const extractTextContent = (): string => { - const today = new Date().toLocaleDateString('en-US', { - year: 'numeric', - month: 'long', - day: 'numeric' + const today = new Date().toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", }); - let content = `${props.title.toUpperCase()}\n${'='.repeat(props.title.length)}\n\nExported ${today}\n\n`; - + let content = `${props.title.toUpperCase()}\n${"=".repeat( + props.title.length + )}\n\nExported ${today}\n\n`; + // Convert data to readable text format if (props.exportData) { content += formatDataAsText(props.exportData); } - + return content; }; const convertToMarkdown = (): string => { - const today = new Date().toLocaleDateString('en-US', { - year: 'numeric', - month: 'long', - day: 'numeric' + const today = new Date().toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", }); let content = `# ${props.title}\n\n*Exported ${today}*\n\n`; - + // Convert data to markdown format if (props.exportData) { content += formatDataAsMarkdown(props.exportData); } - + return content; }; const formatDataAsText = (data: any): string => { // Special handling for different template types - if (data.section === 'tech-charter') { + if (data.section === "tech-charter") { return formatTechCharterAsText(data); - } else if (data.section === 'membership-agreement') { + } else if (data.section === "membership-agreement") { return formatMembershipAgreementAsText(data); - } else if (data.section === 'conflict-resolution-framework') { + } else if (data.section === "conflict-resolution-framework") { return formatConflictResolutionAsText(data); - } else if (data.section === 'decision-framework') { + } else if (data.section === "decision-framework") { return formatDecisionFrameworkAsText(data); } - + if (Array.isArray(data)) { - return data.map((item, index) => `${index + 1}. ${formatObjectAsText(item)}`).join('\n'); - } else if (typeof data === 'object' && data !== null) { + return data + .map((item, index) => `${index + 1}. ${formatObjectAsText(item)}`) + .join("\n"); + } else if (typeof data === "object" && data !== null) { return formatObjectAsText(data); } return String(data); @@ -184,19 +189,21 @@ const formatDataAsText = (data: any): string => { const formatDataAsMarkdown = (data: any): string => { // Special handling for different template types - if (data.section === 'tech-charter') { + if (data.section === "tech-charter") { return formatTechCharterAsMarkdown(data); - } else if (data.section === 'membership-agreement') { + } else if (data.section === "membership-agreement") { return formatMembershipAgreementAsMarkdown(data); - } else if (data.section === 'conflict-resolution-framework') { + } else if (data.section === "conflict-resolution-framework") { return formatConflictResolutionAsMarkdown(data); - } else if (data.section === 'decision-framework') { + } else if (data.section === "decision-framework") { return formatDecisionFrameworkAsMarkdown(data); } - + if (Array.isArray(data)) { - return data.map((item, index) => `${index + 1}. ${formatObjectAsMarkdown(item)}`).join('\n\n'); - } else if (typeof data === 'object' && data !== null) { + return data + .map((item, index) => `${index + 1}. ${formatObjectAsMarkdown(item)}`) + .join("\n\n"); + } else if (typeof data === "object" && data !== null) { return formatObjectAsMarkdown(data); } return String(data); @@ -208,7 +215,7 @@ const formatTechCharterAsText = (data: any): string => { const selectedPrinciples = Object.keys(data.principleWeights).filter( (p) => data.principleWeights[p] > 0 ); - + if (selectedPrinciples.filter((p) => !data.nonNegotiables.includes(p)).length > 0) { content += `CORE PRINCIPLES\n---------------\n\n`; selectedPrinciples @@ -245,7 +252,9 @@ const formatTechCharterAsText = (data: any): string => { if (data.sortedWeights.length > 0) { content += `EVALUATION RUBRIC\n-----------------\n\nScore each vendor option using these weighted criteria (0-5 scale):\n\n`; data.sortedWeights.forEach((principle: any) => { - content += `${principle.name} (Weight: ${data.principleWeights[principle.id]})\n${principle.rubricDescription}\n\n`; + content += `${principle.name} (Weight: ${data.principleWeights[principle.id]})\n${ + principle.rubricDescription + }\n\n`; }); } @@ -258,7 +267,7 @@ const formatTechCharterAsMarkdown = (data: any): string => { const selectedPrinciples = Object.keys(data.principleWeights).filter( (p) => data.principleWeights[p] > 0 ); - + if (selectedPrinciples.filter((p) => !data.nonNegotiables.includes(p)).length > 0) { content += `## Core Principles\n\n`; selectedPrinciples @@ -297,7 +306,9 @@ const formatTechCharterAsMarkdown = (data: any): string => { content += `| Criterion | Description | Weight |\n`; content += `|-----------|-------------|--------|\n`; data.sortedWeights.forEach((principle: any) => { - content += `| ${principle.name} | ${principle.rubricDescription} | ${data.principleWeights[principle.id]} |\n`; + content += `| ${principle.name} | ${principle.rubricDescription} | ${ + data.principleWeights[principle.id] + } |\n`; }); } @@ -305,75 +316,79 @@ const formatTechCharterAsMarkdown = (data: any): string => { }; const formatObjectAsText = (obj: any): string => { - if (!obj || typeof obj !== 'object') return String(obj); - + if (!obj || typeof obj !== "object") return String(obj); + return Object.entries(obj) - .filter(([key, value]) => value !== null && value !== undefined && key !== 'id') + .filter(([key, value]) => value !== null && value !== undefined && key !== "id") .map(([key, value]) => { - const formattedKey = key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase()); - if (typeof value === 'object') { + const formattedKey = key + .replace(/([A-Z])/g, " $1") + .replace(/^./, (str) => str.toUpperCase()); + if (typeof value === "object") { return `${formattedKey}: ${JSON.stringify(value)}`; } return `${formattedKey}: ${value}`; }) - .join('\n'); + .join("\n"); }; const formatObjectAsMarkdown = (obj: any): string => { - if (!obj || typeof obj !== 'object') return String(obj); - + if (!obj || typeof obj !== "object") return String(obj); + return Object.entries(obj) - .filter(([key, value]) => value !== null && value !== undefined && key !== 'id') + .filter(([key, value]) => value !== null && value !== undefined && key !== "id") .map(([key, value]) => { - const formattedKey = key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase()); - if (typeof value === 'object') { + const formattedKey = key + .replace(/([A-Z])/g, " $1") + .replace(/^./, (str) => str.toUpperCase()); + if (typeof value === "object") { return `**${formattedKey}**: \`${JSON.stringify(value)}\``; } return `**${formattedKey}**: ${value}`; }) - .join(' \n'); + .join(" \n"); }; // Membership Agreement formatting const formatMembershipAgreementAsText = (data: any): string => { let content = `MEMBERSHIP AGREEMENT\n====================\n\n`; content += `Cooperative Name: ${data.cooperativeName}\n`; - content += `Member Name: ${data.formData.memberName || '[Member Name]'}\n`; - content += `Effective Date: ${data.formData.effectiveDate || '[Date]'}\n\n`; - + content += `Member Name: ${data.formData.memberName || "[Member Name]"}\n`; + content += `Effective Date: ${data.formData.effectiveDate || "[Date]"}\n\n`; + if (data.formData.purpose) { content += `PURPOSE\n-------\n${data.formData.purpose}\n\n`; } - + if (data.formData.membershipRequirements) { content += `MEMBERSHIP REQUIREMENTS\n----------------------\n${data.formData.membershipRequirements}\n\n`; } - + if (data.formData.rightsAndResponsibilities) { content += `RIGHTS AND RESPONSIBILITIES\n--------------------------\n${data.formData.rightsAndResponsibilities}\n\n`; } - + return content; }; const formatMembershipAgreementAsMarkdown = (data: any): string => { let content = `## Membership Agreement\n\n`; content += `**Cooperative Name:** ${data.cooperativeName} \n`; - content += `**Member Name:** ${data.formData.memberName || '[Member Name]'} \n`; - content += `**Effective Date:** ${data.formData.effectiveDate || '[Date]'} \n\n`; - + content += `**Member Name:** ${data.formData.memberName || "[Member Name]"} \n`; + content += `**Effective Date:** ${data.formData.effectiveDate || "[Date]"} \n\n`; + if (data.formData.purpose) { content += `### Purpose\n\n${data.formData.purpose}\n\n`; } - + if (data.formData.membershipRequirements) { content += `### Membership Requirements\n\n${data.formData.membershipRequirements}\n\n`; } - + if (data.formData.rightsAndResponsibilities) { content += `### Rights and Responsibilities\n\n${data.formData.rightsAndResponsibilities}\n\n`; } - + return content; }; @@ -383,33 +398,35 @@ const formatConflictResolutionAsText = (data: any): string => { content += `Organization: ${data.orgName}\n`; content += `Organization Type: ${data.orgType}\n`; content += `Member Count: ${data.memberCount}\n\n`; - + if (data.coreValues?.length > 0) { content += `CORE VALUES\n-----------\n`; data.coreValues.forEach((value: string, index: number) => { content += `${index + 1}. ${value}\n`; }); - content += '\n'; + content += "\n"; } - + if (data.principles?.length > 0) { content += `PRINCIPLES\n----------\n`; data.principles.forEach((principle: string, index: number) => { content += `${index + 1}. ${principle}\n`; }); - content += '\n'; + content += "\n"; } - + if (data.policies) { content += `POLICIES\n--------\n`; Object.entries(data.policies).forEach(([key, value]) => { if (value) { - const formattedKey = key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase()); + const formattedKey = key + .replace(/([A-Z])/g, " $1") + .replace(/^./, (str) => str.toUpperCase()); content += `${formattedKey}: ${value}\n`; } }); } - + return content; }; @@ -418,40 +435,42 @@ const formatConflictResolutionAsMarkdown = (data: any): string => { content += `**Organization:** ${data.orgName} \n`; content += `**Organization Type:** ${data.orgType} \n`; content += `**Member Count:** ${data.memberCount} \n\n`; - + if (data.coreValues?.length > 0) { content += `### Core Values\n\n`; data.coreValues.forEach((value: string, index: number) => { content += `${index + 1}. ${value}\n`; }); - content += '\n'; + content += "\n"; } - + if (data.principles?.length > 0) { content += `### Principles\n\n`; data.principles.forEach((principle: string, index: number) => { content += `${index + 1}. ${principle}\n`; }); - content += '\n'; + content += "\n"; } - + if (data.policies) { content += `### Policies\n\n`; Object.entries(data.policies).forEach(([key, value]) => { if (value) { - const formattedKey = key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase()); + const formattedKey = key + .replace(/([A-Z])/g, " $1") + .replace(/^./, (str) => str.toUpperCase()); content += `**${formattedKey}:** ${value} \n`; } }); } - + return content; }; // Decision Framework formatting const formatDecisionFrameworkAsText = (data: any): string => { let content = `DECISION FRAMEWORK RESULTS\n=========================\n\n`; - + if (data.surveyResponses) { content += `SURVEY RESPONSES\n----------------\n`; content += `Urgency: ${data.surveyResponses.urgency}\n`; @@ -462,37 +481,37 @@ const formatDecisionFrameworkAsText = (data: any): string => { content += `Investment: ${data.surveyResponses.investment}\n`; content += `Team Size: ${data.surveyResponses.teamSize}\n\n`; } - + if (data.recommendedFramework) { const framework = data.recommendedFramework; content += `RECOMMENDED FRAMEWORK\n--------------------\n`; content += `Method: ${framework.method}\n`; content += `Tagline: ${framework.tagline}\n\n`; content += `Reasoning: ${framework.reasoning}\n\n`; - + if (framework.steps) { content += `IMPLEMENTATION STEPS\n-------------------\n`; framework.steps.forEach((step: string, index: number) => { content += `${index + 1}. ${step}\n`; }); - content += '\n'; + content += "\n"; } - + if (framework.tips) { content += `PRO TIPS\n--------\n`; framework.tips.forEach((tip: string, index: number) => { content += `${index + 1}. ${tip}\n`; }); - content += '\n'; + content += "\n"; } } - + return content; }; const formatDecisionFrameworkAsMarkdown = (data: any): string => { let content = `## Decision Framework Results\n\n`; - + if (data.surveyResponses) { content += `### Survey Responses\n\n`; content += `**Urgency:** ${data.surveyResponses.urgency} \n`; @@ -503,38 +522,38 @@ const formatDecisionFrameworkAsMarkdown = (data: any): string => { content += `**Investment:** ${data.surveyResponses.investment} \n`; content += `**Team Size:** ${data.surveyResponses.teamSize} \n\n`; } - + if (data.recommendedFramework) { const framework = data.recommendedFramework; content += `### Recommended Framework\n\n`; content += `**Method:** ${framework.method} \n`; content += `**Tagline:** ${framework.tagline} \n\n`; content += `**Reasoning:** ${framework.reasoning}\n\n`; - + if (framework.steps) { content += `#### Implementation Steps\n\n`; framework.steps.forEach((step: string, index: number) => { content += `${index + 1}. ${step}\n`; }); - content += '\n'; + content += "\n"; } - + if (framework.tips) { content += `#### Pro Tips\n\n`; framework.tips.forEach((tip: string, index: number) => { content += `${index + 1}. ${tip}\n`; }); - content += '\n'; + content += "\n"; } } - + return content; }; const downloadFile = (content: string, filename: string, type: string) => { const blob = new Blob([content], { type }); const url = URL.createObjectURL(blob); - const a = document.createElement('a'); + const a = document.createElement("a"); a.href = url; a.download = filename; document.body.appendChild(a); @@ -545,31 +564,7 @@ const downloadFile = (content: string, filename: string, type: string) => { \ No newline at end of file + diff --git a/pages/templates/conflict-resolution-framework.vue b/pages/templates/conflict-resolution-framework.vue index 00bc782..450dc98 100644 --- a/pages/templates/conflict-resolution-framework.vue +++ b/pages/templates/conflict-resolution-framework.vue @@ -4,13 +4,12 @@ -
- -
+ +
-
-
@@ -1392,57 +1389,9 @@ const exportData = computed(() => ({ diff --git a/pages/templates/decision-framework.vue b/pages/templates/decision-framework.vue index 5a0a9a3..ac16d47 100644 --- a/pages/templates/decision-framework.vue +++ b/pages/templates/decision-framework.vue @@ -4,34 +4,39 @@ -
- -
+
+ style="font-family: 'Ubuntu', monospace" + >
+ class="bg-white dark:bg-neutral-950 border-2 border-neutral-900 dark:border-neutral-100 decision-framework-container" + >
+ class="bg-black dark:bg-white text-white dark:text-black px-8 py-12 text-center header-section" + >
+ class="absolute top-4 left-4 right-0 bottom-0 dither-shadow-header" + >
+ class="relative bg-black dark:bg-white text-white dark:text-black px-4 py-4 border-2 border-neutral-100 dark:border-neutral-900" + >

+ style="font-family: 'Ubuntu', monospace" + > Decision Framework Helper

@@ -41,24 +46,22 @@

- Step {{ currentStep }} of {{ totalSteps }} - {{ Math.round((currentStep / totalSteps) * 100) }}%
+ class="w-full bg-white dark:bg-black h-2 border-2 border-neutral-100 dark:border-neutral-900" + >
+ }" + >
@@ -72,14 +75,15 @@
+ style="font-family: 'Ubuntu', monospace" + > How urgent is this decision?
+ class="bg-white dark:bg-neutral-950 p-8 border-2 border-neutral-900 dark:border-neutral-100 relative" + > -
+
@@ -101,10 +105,12 @@ min="1" max="5" step="1" - class="w-full h-2 bg-white dark:bg-black appearance-none cursor-pointer slider" /> + class="w-full h-2 bg-white dark:bg-black appearance-none cursor-pointer slider" + />
+ style="font-family: 'Ubuntu Mono', monospace" + > 1 2 3 @@ -120,7 +126,8 @@
+ style="font-family: 'Ubuntu', monospace" + > Can we change our minds later?
@@ -133,7 +140,8 @@ ? 'border-violet-700 bg-violet-700 text-white' : 'border-neutral-200 hover:border-violet-700 hover:bg-violet-50', ]" - @click="selectOption('reversible', option.value)"> + @click="selectOption('reversible', option.value)" + >
{{ option.title }}
{{ option.description }} @@ -157,7 +165,8 @@ ? 'border-violet-700 bg-violet-700 text-white' : 'border-neutral-200 hover:border-violet-700 hover:bg-violet-50', ]" - @click="selectOption('expertise', option.value)"> + @click="selectOption('expertise', option.value)" + >
{{ option.title }}
{{ option.description }} @@ -181,7 +190,8 @@ ? 'border-violet-700 bg-violet-700 text-white' : 'border-neutral-200 hover:border-violet-700 hover:bg-violet-50', ]" - @click="selectOption('impact', option.value)"> + @click="selectOption('impact', option.value)" + >
{{ option.title }}
{{ option.description }} @@ -205,7 +215,8 @@ ? 'border-violet-700 bg-violet-700 text-white' : 'border-neutral-200 hover:border-violet-700 hover:bg-violet-50', ]" - @click="selectOption('options', option.value)"> + @click="selectOption('options', option.value)" + >
{{ option.title }}
{{ option.description }} @@ -229,7 +240,8 @@ ? 'border-violet-700 bg-violet-700 text-white' : 'border-neutral-200 hover:border-violet-700 hover:bg-violet-50', ]" - @click="selectOption('investment', option.value)"> + @click="selectOption('investment', option.value)" + >
{{ option.title }}
{{ option.description }} @@ -253,7 +265,8 @@ ? 'bg-violet-700 text-white border-violet-700' : 'bg-white text-neutral-700 border-neutral-200 hover:border-violet-700 hover:bg-violet-50', ]" - @click="selectOption('teamSize', size)"> + @click="selectOption('teamSize', size)" + > {{ size }}
@@ -261,11 +274,13 @@
+ class="flex justify-between items-center mt-12 pt-8 border-t-2 border-neutral-200" + >
@@ -273,13 +288,15 @@
@@ -289,7 +306,8 @@
+ class="border-t-2 border-neutral-200 pt-12" + >

@@ -317,10 +335,9 @@
  • - + class="flex items-start" + > + {{ step }}
  • @@ -334,10 +351,9 @@
  • - + class="flex items-start" + > + {{ tip }}
  • @@ -351,7 +367,8 @@ variant="soft" :title="'Watch out for:'" :description="result.warning" - class="mb-6" /> + class="mb-6" + /> + class="mb-6" + />

    @@ -369,7 +387,8 @@ + class="bg-white" + > {{ alt.method }}: {{ alt.when }} @@ -380,10 +399,7 @@ Try Another Decision - + Print Recommendation

    @@ -395,17 +411,16 @@
    -
    -
    + title="Decision Framework Helper" + />
    \ No newline at end of file + diff --git a/pages/templates/tech-charter.vue b/pages/templates/tech-charter.vue index 3101e0d..e9ba808 100644 --- a/pages/templates/tech-charter.vue +++ b/pages/templates/tech-charter.vue @@ -4,23 +4,23 @@ -
    - -
    +
    + class="template-wrapper bg-white dark:bg-neutral-950 text-neutral-900 dark:text-neutral-100" + >

    + class="text-3xl md:text-5xl font-bold uppercase text-neutral-900 dark:text-white m-0 py-4 border-t-2 border-b-2 border-neutral-900 dark:border-neutral-100" + > Tech Charter

    @@ -30,12 +30,9 @@
    -

    - Charter Purpose -

    +

    Charter Purpose

    - Describe what this charter will guide and why it matters to - your group. + Describe what this charter will guide and why it matters to your group.

    @@ -43,7 +40,8 @@