style: enhance CSS for improved layout and responsiveness, integrate Ubuntu font, and streamline export options across templates
This commit is contained in:
parent
3b33ff3819
commit
eede87a273
6 changed files with 1130 additions and 1745 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,28 +8,27 @@
|
|||
@click="copyToClipboard"
|
||||
class="export-btn"
|
||||
:disabled="isProcessing"
|
||||
ref="copyButton">
|
||||
ref="copyButton"
|
||||
>
|
||||
<UIcon name="i-heroicons-clipboard" />
|
||||
<span>Copy as Text</span>
|
||||
<UIcon
|
||||
v-if="showCopySuccess"
|
||||
name="i-heroicons-check"
|
||||
class="success-icon" />
|
||||
<UIcon v-if="showCopySuccess" name="i-heroicons-check" class="success-icon" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
@click="downloadAsMarkdown"
|
||||
class="export-btn"
|
||||
:disabled="isProcessing"
|
||||
ref="downloadButton">
|
||||
ref="downloadButton"
|
||||
>
|
||||
<UIcon name="i-heroicons-arrow-down-tray" />
|
||||
<span>Download Markdown</span>
|
||||
<UIcon
|
||||
v-if="showDownloadSuccess"
|
||||
name="i-heroicons-check"
|
||||
class="success-icon" />
|
||||
class="success-icon"
|
||||
/>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -45,9 +44,9 @@ interface Props {
|
|||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
filename: 'export',
|
||||
title: 'Export Data',
|
||||
containerClass: 'centered'
|
||||
filename: "export",
|
||||
title: "Export Data",
|
||||
containerClass: "centered",
|
||||
});
|
||||
|
||||
const isProcessing = ref(false);
|
||||
|
|
@ -58,18 +57,21 @@ const copyButton = ref<HTMLButtonElement>();
|
|||
const downloadButton = ref<HTMLButtonElement>();
|
||||
|
||||
// Success feedback animation
|
||||
const showSuccessFeedback = (buttonRef: Ref<HTMLButtonElement | undefined>, successRef: Ref<boolean>) => {
|
||||
const showSuccessFeedback = (
|
||||
buttonRef: Ref<HTMLButtonElement | undefined>,
|
||||
successRef: Ref<boolean>
|
||||
) => {
|
||||
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);
|
||||
};
|
||||
|
||||
|
|
@ -85,26 +87,26 @@ const copyToClipboard = async () => {
|
|||
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;
|
||||
}
|
||||
|
|
@ -117,25 +119,26 @@ const downloadAsMarkdown = () => {
|
|||
|
||||
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) {
|
||||
|
|
@ -146,10 +149,10 @@ const extractTextContent = (): string => {
|
|||
};
|
||||
|
||||
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`;
|
||||
|
|
@ -164,19 +167,21 @@ const convertToMarkdown = (): string => {
|
|||
|
||||
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);
|
||||
|
|
@ -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`;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -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,41 +316,45 @@ 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`;
|
||||
|
|
@ -359,8 +374,8 @@ const formatMembershipAgreementAsText = (data: any): string => {
|
|||
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`;
|
||||
|
|
@ -389,7 +404,7 @@ const formatConflictResolutionAsText = (data: any): string => {
|
|||
data.coreValues.forEach((value: string, index: number) => {
|
||||
content += `${index + 1}. ${value}\n`;
|
||||
});
|
||||
content += '\n';
|
||||
content += "\n";
|
||||
}
|
||||
|
||||
if (data.principles?.length > 0) {
|
||||
|
|
@ -397,14 +412,16 @@ const formatConflictResolutionAsText = (data: any): string => {
|
|||
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`;
|
||||
}
|
||||
});
|
||||
|
|
@ -424,7 +441,7 @@ const formatConflictResolutionAsMarkdown = (data: any): string => {
|
|||
data.coreValues.forEach((value: string, index: number) => {
|
||||
content += `${index + 1}. ${value}\n`;
|
||||
});
|
||||
content += '\n';
|
||||
content += "\n";
|
||||
}
|
||||
|
||||
if (data.principles?.length > 0) {
|
||||
|
|
@ -432,14 +449,16 @@ const formatConflictResolutionAsMarkdown = (data: any): string => {
|
|||
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`;
|
||||
}
|
||||
});
|
||||
|
|
@ -475,7 +494,7 @@ const formatDecisionFrameworkAsText = (data: any): string => {
|
|||
framework.steps.forEach((step: string, index: number) => {
|
||||
content += `${index + 1}. ${step}\n`;
|
||||
});
|
||||
content += '\n';
|
||||
content += "\n";
|
||||
}
|
||||
|
||||
if (framework.tips) {
|
||||
|
|
@ -483,7 +502,7 @@ const formatDecisionFrameworkAsText = (data: any): string => {
|
|||
framework.tips.forEach((tip: string, index: number) => {
|
||||
content += `${index + 1}. ${tip}\n`;
|
||||
});
|
||||
content += '\n';
|
||||
content += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -516,7 +535,7 @@ const formatDecisionFrameworkAsMarkdown = (data: any): string => {
|
|||
framework.steps.forEach((step: string, index: number) => {
|
||||
content += `${index + 1}. ${step}\n`;
|
||||
});
|
||||
content += '\n';
|
||||
content += "\n";
|
||||
}
|
||||
|
||||
if (framework.tips) {
|
||||
|
|
@ -524,7 +543,7 @@ const formatDecisionFrameworkAsMarkdown = (data: any): string => {
|
|||
framework.tips.forEach((tip: string, index: number) => {
|
||||
content += `${index + 1}. ${tip}\n`;
|
||||
});
|
||||
content += '\n';
|
||||
content += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -534,7 +553,7 @@ const formatDecisionFrameworkAsMarkdown = (data: any): string => {
|
|||
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) => {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
.export-options {
|
||||
margin: 2rem auto;
|
||||
background: white;
|
||||
border: 2px solid #000;
|
||||
position: relative;
|
||||
box-shadow: 4px 4px 0px #000;
|
||||
}
|
||||
|
||||
.export-options.centered {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.export-options::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
right: -2px;
|
||||
bottom: -2px;
|
||||
background: #000;
|
||||
background-image: radial-gradient(white 1px, transparent 1px);
|
||||
background-size: 3px 3px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@reference "tailwindcss";
|
||||
.export-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
|
@ -592,31 +587,8 @@ const downloadFile = (content: string, filename: string, type: string) => {
|
|||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.export-title {
|
||||
font-weight: 700;
|
||||
color: #374151;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.export-btn {
|
||||
background: #f9fafb;
|
||||
border: 2px solid #000;
|
||||
color: #374151;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 0;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
min-width: fit-content;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@apply bg-neutral-100 dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 text-neutral-900 dark:text-white;
|
||||
}
|
||||
|
||||
.export-btn:hover:not(:disabled) {
|
||||
|
|
@ -667,33 +639,6 @@ const downloadFile = (content: string, filename: string, type: string) => {
|
|||
transform: translateY(-50%) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark mode styles */
|
||||
html.dark .export-options {
|
||||
background: #0a0a0a;
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
html.dark .export-options::before {
|
||||
background: white;
|
||||
background-image: radial-gradient(black 1px, transparent 1px);
|
||||
}
|
||||
|
||||
html.dark .export-title {
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
html.dark .export-btn {
|
||||
background: #0a0a0a;
|
||||
border-color: white;
|
||||
color: white;
|
||||
}
|
||||
|
||||
html.dark .export-btn:hover:not(:disabled) {
|
||||
background: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* Mobile responsive */
|
||||
@media (max-width: 768px) {
|
||||
.export-content {
|
||||
|
|
|
|||
|
|
@ -4,13 +4,12 @@
|
|||
<WizardSubnav />
|
||||
|
||||
<!-- Export Options - Top -->
|
||||
<div class="flex justify-center py-6">
|
||||
|
||||
<ExportOptions
|
||||
:export-data="exportData"
|
||||
filename="conflict-resolution-framework"
|
||||
title="Conflict Resolution Framework"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="template-wrapper bg-white dark:bg-neutral-950 text-neutral-900 dark:text-neutral-100"
|
||||
|
|
@ -822,14 +821,12 @@
|
|||
</div>
|
||||
|
||||
<!-- Export Options - Bottom -->
|
||||
<div class="flex justify-center py-6">
|
||||
<ExportOptions
|
||||
:export-data="exportData"
|
||||
filename="conflict-resolution-framework"
|
||||
title="Conflict Resolution Framework"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
|
@ -1392,57 +1389,9 @@ const exportData = computed(() => ({
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 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");
|
||||
@reference "tailwindcss";
|
||||
|
||||
/* Template wrapper and document styling */
|
||||
/* rely on Tailwind bg utilities applied on wrapper */
|
||||
.template-wrapper {
|
||||
min-height: 100vh;
|
||||
padding: 2rem;
|
||||
font-family: "Ubuntu", monospace;
|
||||
position: relative;
|
||||
}
|
||||
.template-wrapper::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-image: radial-gradient(circle at 25% 25%, black 1px, transparent 1px),
|
||||
radial-gradient(circle at 75% 75%, black 1px, transparent 1px);
|
||||
background-size: 8px 8px, 8px 8px;
|
||||
background-position: 0 0, 4px 4px;
|
||||
opacity: 0.1;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.document-page::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;
|
||||
}
|
||||
.document-page::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border: 1px solid black;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
/* Template-specific styles that aren't in main.css */
|
||||
|
||||
/* Progress bar */
|
||||
.progress-bar {
|
||||
|
|
@ -1461,30 +1410,6 @@ const exportData = computed(() => ({
|
|||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
/* Document header */
|
||||
.document-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.document-title {
|
||||
font-size: 2.25rem;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
|
||||
margin: 0 0 0.5rem;
|
||||
padding: 1rem 0;
|
||||
border-top: 2px solid #111827;
|
||||
border-bottom: 2px solid #111827;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: #666;
|
||||
font-size: 1.1rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Quick start section */
|
||||
.quick-start {
|
||||
background: #f0f7ff;
|
||||
|
|
@ -1528,11 +1453,6 @@ const exportData = computed(() => ({
|
|||
color: white;
|
||||
}
|
||||
|
||||
/* Section styling */
|
||||
.section-card {
|
||||
@apply border border-neutral-200 dark:border-neutral-800 rounded-lg p-5 mb-8;
|
||||
}
|
||||
|
||||
/* Styling for sections when toggled off */
|
||||
.section-card:has(.space-y-6[style*="display: none"]) {
|
||||
opacity: 0.7;
|
||||
|
|
@ -1542,17 +1462,6 @@ const exportData = computed(() => ({
|
|||
color: #666;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
/* presentational container only */
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 800;
|
||||
color: inherit;
|
||||
margin: 0 0 1rem 0;
|
||||
}
|
||||
|
||||
/* Toggle styling */
|
||||
.toggle-section {
|
||||
display: flex;
|
||||
|
|
@ -1589,84 +1498,6 @@ const exportData = computed(() => ({
|
|||
transform: translateX(24px);
|
||||
}
|
||||
|
||||
/* Form styling */
|
||||
.form-group-large {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
/* Ensure form field containers are also full-width */
|
||||
.form-group-large {
|
||||
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;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
/* moved to inline classes */
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
@apply flex flex-col space-y-3;
|
||||
}
|
||||
|
||||
.checkbox-item {
|
||||
@apply flex;
|
||||
}
|
||||
|
||||
/* Approach radio styling removed - using pure Tailwind classes */
|
||||
|
||||
.values-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 0.75rem;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.content-paragraph {
|
||||
margin-bottom: 0.75rem;
|
||||
line-height: 1.6;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Validation error styling */
|
||||
.validation-error {
|
||||
color: #ef4444;
|
||||
|
|
@ -1702,21 +1533,6 @@ const exportData = computed(() => ({
|
|||
background: #5856eb;
|
||||
}
|
||||
|
||||
/* Bottom export controls */
|
||||
|
||||
.export-buttons-bottom {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.export-btn.large svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.close-preview-btn {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
|
|
@ -1857,169 +1673,4 @@ html.dark .policy-preview code {
|
|||
.policy-preview em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Export controls */
|
||||
|
||||
.export-btn {
|
||||
/* retained for bitmap overrides below; core layout now via Tailwind */
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
.document-page {
|
||||
max-width: none !important;
|
||||
box-shadow: none !important;
|
||||
border-radius: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.document-title {
|
||||
font-size: 18pt;
|
||||
background: none;
|
||||
padding: 0.5rem 0;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 14pt;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.section-card {
|
||||
break-inside: avoid;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.checkbox-item {
|
||||
font-size: 10pt;
|
||||
margin: 2pt 0;
|
||||
}
|
||||
|
||||
.inline-field {
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
border-bottom: 1pt solid #000 !important;
|
||||
padding: 2pt !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Bitmap aesthetic overrides - remove all rounded corners */
|
||||
* {
|
||||
border-radius: 0 !important;
|
||||
font-family: "Ubuntu", monospace !important;
|
||||
}
|
||||
|
||||
/* Form fields with bitmap styling */
|
||||
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;
|
||||
}
|
||||
|
||||
.export-btn {
|
||||
@apply bg-white border border-black text-black font-mono uppercase font-normal tracking-wide;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
/* Remove any card styling roundness */
|
||||
.section-card,
|
||||
.form-group-large {
|
||||
border-radius: 0 !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;
|
||||
}
|
||||
|
||||
/* Mobile responsiveness */
|
||||
@media (max-width: 768px) {
|
||||
.template-wrapper {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.preset-buttons {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.values-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.export-content {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -4,34 +4,39 @@
|
|||
<WizardSubnav />
|
||||
|
||||
<!-- Export Options at Top -->
|
||||
<div class="flex justify-center py-6">
|
||||
<ExportOptions
|
||||
:export-data="exportData"
|
||||
filename="decision-framework"
|
||||
title="Decision Framework Helper" />
|
||||
</div>
|
||||
title="Decision Framework Helper"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="template-wrapper bg-white dark:bg-neutral-950 text-neutral-900 dark:text-neutral-100"
|
||||
style="font-family: 'Ubuntu', monospace">
|
||||
style="font-family: 'Ubuntu', monospace"
|
||||
>
|
||||
<!-- Spacer for consistency -->
|
||||
<div class="py-4"></div>
|
||||
|
||||
<div class="max-w-4xl mx-auto relative px-4">
|
||||
<div
|
||||
class="bg-white dark:bg-neutral-950 border-2 border-neutral-900 dark:border-neutral-100 decision-framework-container">
|
||||
class="bg-white dark:bg-neutral-950 border-2 border-neutral-900 dark:border-neutral-100 decision-framework-container"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div
|
||||
class="bg-black dark:bg-white text-white dark:text-black px-8 py-12 text-center header-section">
|
||||
class="bg-black dark:bg-white text-white dark:text-black px-8 py-12 text-center header-section"
|
||||
>
|
||||
<!-- Dithered shadow background -->
|
||||
<div
|
||||
class="absolute top-4 left-4 right-0 bottom-0 dither-shadow-header"></div>
|
||||
class="absolute top-4 left-4 right-0 bottom-0 dither-shadow-header"
|
||||
></div>
|
||||
|
||||
<div
|
||||
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">
|
||||
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"
|
||||
>
|
||||
<h1
|
||||
class="text-3xl font-bold mb-2 uppercase"
|
||||
style="font-family: 'Ubuntu', monospace">
|
||||
style="font-family: 'Ubuntu', monospace"
|
||||
>
|
||||
Decision Framework Helper
|
||||
</h1>
|
||||
<p class="text-lg" style="font-family: 'Ubuntu', monospace">
|
||||
|
|
@ -41,24 +46,22 @@
|
|||
<!-- Progress Bar -->
|
||||
<div v-if="!showResult" class="mt-8">
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<span
|
||||
class="text-sm"
|
||||
style="font-family: 'Ubuntu Mono', monospace"
|
||||
<span class="text-sm" style="font-family: 'Ubuntu Mono', monospace"
|
||||
>Step {{ currentStep }} of {{ totalSteps }}</span
|
||||
>
|
||||
<span
|
||||
class="text-sm"
|
||||
style="font-family: 'Ubuntu Mono', monospace"
|
||||
<span class="text-sm" style="font-family: 'Ubuntu Mono', monospace"
|
||||
>{{ Math.round((currentStep / totalSteps) * 100) }}%</span
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="w-full bg-white dark:bg-black h-2 border-2 border-neutral-100 dark:border-neutral-900">
|
||||
class="w-full bg-white dark:bg-black h-2 border-2 border-neutral-100 dark:border-neutral-900"
|
||||
>
|
||||
<div
|
||||
class="bg-black dark:bg-white h-full transition-all duration-300 progress-dither"
|
||||
:style="{
|
||||
width: (currentStep / totalSteps) * 100 + '%',
|
||||
}"></div>
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -72,14 +75,15 @@
|
|||
<div v-if="currentStep === 1">
|
||||
<div
|
||||
class="font-semibold text-black dark:text-white mb-6 text-2xl"
|
||||
style="font-family: 'Ubuntu', monospace">
|
||||
style="font-family: 'Ubuntu', monospace"
|
||||
>
|
||||
How urgent is this decision?
|
||||
</div>
|
||||
<div
|
||||
class="bg-white dark:bg-neutral-950 p-8 border-2 border-neutral-900 dark:border-neutral-100 relative">
|
||||
class="bg-white dark:bg-neutral-950 p-8 border-2 border-neutral-900 dark:border-neutral-100 relative"
|
||||
>
|
||||
<!-- Dithered shadow background -->
|
||||
<div
|
||||
class="absolute top-2 left-2 right-0 bottom-0 dither-shadow"></div>
|
||||
<div class="absolute top-2 left-2 right-0 bottom-0 dither-shadow"></div>
|
||||
|
||||
<div class="relative">
|
||||
<div class="flex justify-between mb-6 text-sm">
|
||||
|
|
@ -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"
|
||||
/>
|
||||
<div
|
||||
class="flex justify-between mt-4 text-sm text-black dark:text-white"
|
||||
style="font-family: 'Ubuntu Mono', monospace">
|
||||
style="font-family: 'Ubuntu Mono', monospace"
|
||||
>
|
||||
<span>1</span>
|
||||
<span>2</span>
|
||||
<span>3</span>
|
||||
|
|
@ -120,7 +126,8 @@
|
|||
<div v-if="currentStep === 2">
|
||||
<div
|
||||
class="font-semibold text-black mb-6 text-2xl"
|
||||
style="font-family: 'Ubuntu', monospace">
|
||||
style="font-family: 'Ubuntu', monospace"
|
||||
>
|
||||
Can we change our minds later?
|
||||
</div>
|
||||
<div class="grid gap-4">
|
||||
|
|
@ -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)"
|
||||
>
|
||||
<div class="font-semibold mb-1">{{ option.title }}</div>
|
||||
<div class="text-sm opacity-85">
|
||||
{{ 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)"
|
||||
>
|
||||
<div class="font-semibold mb-1">{{ option.title }}</div>
|
||||
<div class="text-sm opacity-85">
|
||||
{{ 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)"
|
||||
>
|
||||
<div class="font-semibold mb-1">{{ option.title }}</div>
|
||||
<div class="text-sm opacity-85">
|
||||
{{ 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)"
|
||||
>
|
||||
<div class="font-semibold mb-1">{{ option.title }}</div>
|
||||
<div class="text-sm opacity-85">
|
||||
{{ 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)"
|
||||
>
|
||||
<div class="font-semibold mb-1">{{ option.title }}</div>
|
||||
<div class="text-sm opacity-85">
|
||||
{{ 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 }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -261,11 +274,13 @@
|
|||
|
||||
<!-- Navigation -->
|
||||
<div
|
||||
class="flex justify-between items-center mt-12 pt-8 border-t-2 border-neutral-200">
|
||||
class="flex justify-between items-center mt-12 pt-8 border-t-2 border-neutral-200"
|
||||
>
|
||||
<button
|
||||
v-if="currentStep > 1"
|
||||
@click="previousStep"
|
||||
class="px-6 py-3 text-violet-700 border-2 border-violet-700 rounded-md hover:bg-violet-50 transition-all duration-200">
|
||||
class="px-6 py-3 text-violet-700 border-2 border-violet-700 rounded-md hover:bg-violet-50 transition-all duration-200"
|
||||
>
|
||||
← Previous
|
||||
</button>
|
||||
<div v-else></div>
|
||||
|
|
@ -273,13 +288,15 @@
|
|||
<button
|
||||
v-if="canProceed && currentStep < totalSteps"
|
||||
@click="nextStep"
|
||||
class="px-6 py-3 bg-violet-700 text-white rounded-md hover:bg-violet-800 transition-all duration-200">
|
||||
class="px-6 py-3 bg-violet-700 text-white rounded-md hover:bg-violet-800 transition-all duration-200"
|
||||
>
|
||||
Next →
|
||||
</button>
|
||||
<button
|
||||
v-else-if="canProceed && currentStep === totalSteps"
|
||||
@click="showRecommendation"
|
||||
class="px-6 py-3 bg-violet-700 text-white rounded-md hover:bg-violet-800 transition-all duration-200">
|
||||
class="px-6 py-3 bg-violet-700 text-white rounded-md hover:bg-violet-800 transition-all duration-200"
|
||||
>
|
||||
Get Recommendation
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -289,7 +306,8 @@
|
|||
<div
|
||||
v-if="showResult"
|
||||
data-results
|
||||
class="border-t-2 border-neutral-200 pt-12">
|
||||
class="border-t-2 border-neutral-200 pt-12"
|
||||
>
|
||||
<UCard class="bg-neutral-50">
|
||||
<div class="mb-8">
|
||||
<h2 class="text-2xl font-semibold text-violet-700 mb-2">
|
||||
|
|
@ -317,10 +335,9 @@
|
|||
<li
|
||||
v-for="step in result.steps"
|
||||
:key="step"
|
||||
class="flex items-start">
|
||||
<span class="text-violet-700 font-bold mr-3 mt-1"
|
||||
>→</span
|
||||
class="flex items-start"
|
||||
>
|
||||
<span class="text-violet-700 font-bold mr-3 mt-1">→</span>
|
||||
<span class="text-neutral-700">{{ step }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
@ -334,10 +351,9 @@
|
|||
<li
|
||||
v-for="tip in result.tips"
|
||||
:key="tip"
|
||||
class="flex items-start">
|
||||
<span class="text-violet-700 font-bold mr-3 mt-1"
|
||||
>→</span
|
||||
class="flex items-start"
|
||||
>
|
||||
<span class="text-violet-700 font-bold mr-3 mt-1">→</span>
|
||||
<span class="text-neutral-700">{{ tip }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
@ -351,7 +367,8 @@
|
|||
variant="soft"
|
||||
:title="'Watch out for:'"
|
||||
:description="result.warning"
|
||||
class="mb-6" />
|
||||
class="mb-6"
|
||||
/>
|
||||
|
||||
<UAlert
|
||||
v-if="result.success"
|
||||
|
|
@ -359,7 +376,8 @@
|
|||
variant="soft"
|
||||
:title="'Success looks like:'"
|
||||
:description="result.success"
|
||||
class="mb-6" />
|
||||
class="mb-6"
|
||||
/>
|
||||
|
||||
<UCard v-if="result.alternatives" class="bg-neutral-50">
|
||||
<h3 class="font-semibold text-neutral-900 mb-4 text-lg">
|
||||
|
|
@ -369,7 +387,8 @@
|
|||
<UCard
|
||||
v-for="alt in result.alternatives"
|
||||
:key="alt.method"
|
||||
class="bg-white">
|
||||
class="bg-white"
|
||||
>
|
||||
<span class="font-semibold">{{ alt.method }}:</span>
|
||||
{{ alt.when }}
|
||||
</UCard>
|
||||
|
|
@ -380,10 +399,7 @@
|
|||
<UButton @click="resetForm" color="violet">
|
||||
Try Another Decision
|
||||
</UButton>
|
||||
<UButton
|
||||
@click="printResult"
|
||||
variant="outline"
|
||||
color="violet">
|
||||
<UButton @click="printResult" variant="outline" color="violet">
|
||||
Print Recommendation
|
||||
</UButton>
|
||||
</div>
|
||||
|
|
@ -395,17 +411,16 @@
|
|||
</div>
|
||||
|
||||
<!-- Export Options at Bottom -->
|
||||
<div class="flex justify-center py-6">
|
||||
<ExportOptions
|
||||
:export-data="exportData"
|
||||
filename="decision-framework"
|
||||
title="Decision Framework Helper" />
|
||||
</div>
|
||||
title="Decision Framework Helper"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import ExportOptions from '~/components/ExportOptions.vue'
|
||||
import ExportOptions from "~/components/ExportOptions.vue";
|
||||
|
||||
const state = reactive({
|
||||
urgency: 3,
|
||||
|
|
@ -584,8 +599,7 @@ function determineFramework() {
|
|||
"Execute without delay",
|
||||
"Debrief when crisis passes",
|
||||
],
|
||||
warning:
|
||||
"Only use in true emergencies. Follow up with team discussion afterward.",
|
||||
warning: "Only use in true emergencies. Follow up with team discussion afterward.",
|
||||
success:
|
||||
"Crisis averted through quick action. Team understands why autocratic mode was necessary.",
|
||||
};
|
||||
|
|
@ -621,11 +635,7 @@ function determineFramework() {
|
|||
}
|
||||
|
||||
// AVOIDANT - non-urgent + undefined + low investment
|
||||
if (
|
||||
state.urgency <= 2 &&
|
||||
state.options === "undefined" &&
|
||||
state.investment === "low"
|
||||
) {
|
||||
if (state.urgency <= 2 && state.options === "undefined" && state.investment === "low") {
|
||||
return {
|
||||
method: "Strategic Delay",
|
||||
tagline: "Wait for clarity to emerge",
|
||||
|
|
@ -638,10 +648,8 @@ function determineFramework() {
|
|||
"Revisit when conditions change",
|
||||
"Document why you're waiting",
|
||||
],
|
||||
warning:
|
||||
"Don't let avoidance become paralysis. Set a deadline for revisiting.",
|
||||
success:
|
||||
"By waiting, better options emerged or the decision became unnecessary.",
|
||||
warning: "Don't let avoidance become paralysis. Set a deadline for revisiting.",
|
||||
success: "By waiting, better options emerged or the decision became unnecessary.",
|
||||
alternatives: [
|
||||
{
|
||||
method: "Time-boxed exploration",
|
||||
|
|
@ -752,8 +760,7 @@ function determineFramework() {
|
|||
"Document all input received",
|
||||
"Explain how input influenced the decision",
|
||||
],
|
||||
success:
|
||||
"Decision informed by diverse perspectives with clear accountability.",
|
||||
success: "Decision informed by diverse perspectives with clear accountability.",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -815,10 +822,7 @@ function determineFramework() {
|
|||
}
|
||||
|
||||
// DEMOCRATIC VOTE - larger group, time pressure
|
||||
if (
|
||||
(state.teamSize === "6-8" || state.teamSize === "9+") &&
|
||||
state.urgency >= 4
|
||||
) {
|
||||
if ((state.teamSize === "6-8" || state.teamSize === "9+") && state.urgency >= 4) {
|
||||
return {
|
||||
method: "Democratic Vote",
|
||||
tagline: "Majority decides, move forward together",
|
||||
|
|
@ -838,8 +842,7 @@ function determineFramework() {
|
|||
],
|
||||
warning:
|
||||
"Don't vote on everything! Reserve it for when other methods are too slow.",
|
||||
success:
|
||||
"Clear decision made efficiently with everyone having equal say.",
|
||||
success: "Clear decision made efficiently with everyone having equal say.",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -940,7 +943,7 @@ const exportData = computed(() => ({
|
|||
state: state,
|
||||
currentStep: currentStep.value,
|
||||
showResult: showResult.value,
|
||||
result: result.value
|
||||
result: result.value,
|
||||
},
|
||||
surveyResponses: {
|
||||
urgency: state.urgency,
|
||||
|
|
@ -949,19 +952,19 @@ const exportData = computed(() => ({
|
|||
impact: state.impact,
|
||||
options: state.options,
|
||||
investment: state.investment,
|
||||
teamSize: state.teamSize
|
||||
teamSize: state.teamSize,
|
||||
},
|
||||
recommendedFramework: result.value || null,
|
||||
metadata: {
|
||||
completedAt: showResult.value ? new Date().toISOString() : null,
|
||||
totalSteps: totalSteps,
|
||||
progressPercentage: Math.round((currentStep.value / totalSteps) * 100)
|
||||
progressPercentage: Math.round((currentStep.value / totalSteps) * 100),
|
||||
},
|
||||
exportedAt: new Date().toISOString(),
|
||||
section: "decision-framework",
|
||||
title: "Decision Framework Helper",
|
||||
description: "Interactive wizard to find the right way to decide together"
|
||||
}))
|
||||
description: "Interactive wizard to find the right way to decide together",
|
||||
}));
|
||||
|
||||
// Keyboard navigation
|
||||
onMounted(() => {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue