336 lines
11 KiB
HTML
336 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{{ filename }} - Ops Template</title>
|
|
<style>
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
body {
|
|
font-family: system-ui, -apple-system, sans-serif;
|
|
background: #f8fafc;
|
|
color: #1e293b;
|
|
line-height: 1.6;
|
|
}
|
|
.container { max-width: 800px; margin: 0 auto; padding: 2rem 1rem; }
|
|
header { margin-bottom: 1.5rem; }
|
|
.breadcrumb { font-size: 0.9rem; color: #64748b; margin-bottom: 0.5rem; }
|
|
.breadcrumb a { color: #15803d; text-decoration: none; }
|
|
.breadcrumb a:hover { text-decoration: underline; }
|
|
h1 { font-size: 1.5rem; color: #15803d; }
|
|
.meta {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
margin-top: 0.5rem;
|
|
font-size: 0.8rem;
|
|
}
|
|
.meta span {
|
|
background: #f1f5f9;
|
|
padding: 0.2rem 0.5rem;
|
|
border-radius: 4px;
|
|
color: #64748b;
|
|
}
|
|
.meta .sample { background: #fef3c7; color: #92400e; }
|
|
|
|
/* Form-like styling */
|
|
.form-card {
|
|
background: white;
|
|
border-radius: 12px;
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
overflow: hidden;
|
|
}
|
|
.form-header {
|
|
background: linear-gradient(135deg, #15803d, #22c55e);
|
|
color: white;
|
|
padding: 1rem 1.5rem;
|
|
}
|
|
.form-header h2 { font-size: 1.1rem; font-weight: 600; }
|
|
.form-body { padding: 1.5rem; }
|
|
|
|
.field {
|
|
margin-bottom: 1.25rem;
|
|
}
|
|
.field:last-child { margin-bottom: 0; }
|
|
.field-label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
color: #64748b;
|
|
margin-bottom: 0.35rem;
|
|
}
|
|
.field-label .edit-icon {
|
|
cursor: pointer;
|
|
opacity: 0.5;
|
|
font-size: 0.85rem;
|
|
}
|
|
.field-label .edit-icon:hover {
|
|
opacity: 1;
|
|
}
|
|
.field-value {
|
|
background: #f8fafc;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 6px;
|
|
padding: 0.75rem;
|
|
font-size: 0.95rem;
|
|
color: #1e293b;
|
|
min-height: 2.5rem;
|
|
}
|
|
.field-value:focus-within {
|
|
border-color: #15803d;
|
|
outline: none;
|
|
box-shadow: 0 0 0 3px rgba(21, 128, 61, 0.1);
|
|
}
|
|
.field-value.multiline {
|
|
min-height: 4rem;
|
|
}
|
|
.field-value ul, .field-value ol {
|
|
margin: 0;
|
|
padding-left: 1.25rem;
|
|
}
|
|
.field-value li {
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
.field-value p {
|
|
margin: 0 0 0.5rem 0;
|
|
}
|
|
.field-value p:last-child { margin-bottom: 0; }
|
|
.field-textarea {
|
|
display: none;
|
|
width: 100%;
|
|
min-height: 100px;
|
|
padding: 0.75rem;
|
|
font-size: 0.95rem;
|
|
font-family: inherit;
|
|
border: 1px solid #15803d;
|
|
border-radius: 6px;
|
|
resize: vertical;
|
|
box-shadow: 0 0 0 3px rgba(21, 128, 61, 0.1);
|
|
}
|
|
.field.editing .field-value { display: none; }
|
|
.field.editing .field-textarea { display: block; }
|
|
|
|
/* Special field styles */
|
|
.field-steps .field-value {
|
|
counter-reset: step;
|
|
padding-left: 0.5rem;
|
|
}
|
|
.field-steps ol {
|
|
list-style: none;
|
|
padding-left: 0;
|
|
}
|
|
.field-steps li {
|
|
position: relative;
|
|
padding-left: 2rem;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
.field-steps li::before {
|
|
counter-increment: step;
|
|
content: counter(step);
|
|
position: absolute;
|
|
left: 0;
|
|
width: 1.5rem;
|
|
height: 1.5rem;
|
|
background: #15803d;
|
|
color: white;
|
|
border-radius: 50%;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.field-problems .field-value {
|
|
background: #fef2f2;
|
|
border-color: #fecaca;
|
|
}
|
|
.field-problems li::before {
|
|
content: '!';
|
|
color: #dc2626;
|
|
font-weight: bold;
|
|
margin-right: 0.5rem;
|
|
}
|
|
|
|
.field-special .field-value {
|
|
background: #fffbeb;
|
|
border-color: #fde68a;
|
|
}
|
|
|
|
.field-technical .field-value {
|
|
background: #f0fdf4;
|
|
border-color: #bbf7d0;
|
|
font-family: 'JetBrains Mono', monospace;
|
|
font-size: 0.85rem;
|
|
}
|
|
.field-technical code {
|
|
background: #dcfce7;
|
|
padding: 0.1rem 0.35rem;
|
|
border-radius: 3px;
|
|
font-size: 0.85em;
|
|
}
|
|
|
|
.sidebar {
|
|
margin-top: 1.5rem;
|
|
padding: 1rem;
|
|
background: #f0fdf4;
|
|
border: 1px solid #bbf7d0;
|
|
border-radius: 8px;
|
|
}
|
|
.sidebar h3 { font-size: 0.8rem; color: #15803d; margin-bottom: 0.75rem; text-transform: uppercase; }
|
|
.sidebar a {
|
|
display: block;
|
|
color: #15803d;
|
|
text-decoration: none;
|
|
padding: 0.35rem 0;
|
|
font-size: 0.9rem;
|
|
}
|
|
.sidebar a:hover { text-decoration: underline; }
|
|
|
|
footer {
|
|
margin-top: 2rem;
|
|
padding-top: 1rem;
|
|
border-top: 1px solid #e2e8f0;
|
|
font-size: 0.85rem;
|
|
}
|
|
footer a { color: #15803d; text-decoration: none; }
|
|
footer a:hover { text-decoration: underline; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<header>
|
|
<div class="breadcrumb">
|
|
<a href="/">Album</a> / <a href="/book/ops-templates/">Ops Templates</a> / {{ user_type }}
|
|
</div>
|
|
<h1>{{ filename.replace('.md', '').replace('-', ' ').title() }}</h1>
|
|
<div class="meta">
|
|
<span>{{ user_type }}</span>
|
|
<span class="sample">Sample</span>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="form-card">
|
|
<div class="form-header">
|
|
<h2>User Flow Template</h2>
|
|
</div>
|
|
<div class="form-body" id="form-content">
|
|
<!-- Content will be parsed and inserted here -->
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sidebar">
|
|
<h3>Related</h3>
|
|
<a href="/book/gherkin/es/{{ user_type }}/{{ filename.replace('.md', '.feature') }}">Gherkin (Spanish)</a>
|
|
<a href="/book/gherkin/en/{{ user_type }}/{{ filename.replace('.md', '.feature') }}">Gherkin (English)</a>
|
|
<a href="/book/feature-flow/">Feature Flow Pipeline</a>
|
|
</div>
|
|
|
|
<footer>
|
|
<a href="/book/ops-templates/">← All Templates</a>
|
|
</footer>
|
|
</div>
|
|
|
|
<script>
|
|
const rawContent = {{ content | tojson }};
|
|
|
|
function parseMarkdown(md) {
|
|
const sections = {};
|
|
let currentSection = null;
|
|
let currentContent = [];
|
|
|
|
const lines = md.split('\n');
|
|
|
|
for (const line of lines) {
|
|
if (line.startsWith('## ')) {
|
|
if (currentSection) {
|
|
sections[currentSection] = currentContent.join('\n').trim();
|
|
}
|
|
currentSection = line.replace('## ', '').trim();
|
|
currentContent = [];
|
|
} else if (line.startsWith('# ')) {
|
|
sections['_title'] = line.replace('# ', '').trim();
|
|
} else if (currentSection) {
|
|
currentContent.push(line);
|
|
}
|
|
}
|
|
if (currentSection) {
|
|
sections[currentSection] = currentContent.join('\n').trim();
|
|
}
|
|
return sections;
|
|
}
|
|
|
|
function formatContent(content) {
|
|
if (!content) return '';
|
|
|
|
// Convert markdown lists to HTML
|
|
let html = content
|
|
.replace(/^(\d+)\.\s+(.*)$/gm, '<li>$2</li>')
|
|
.replace(/^-\s+(.*)$/gm, '<li>$1</li>')
|
|
.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
|
|
// Wrap consecutive li elements in ul/ol
|
|
if (html.includes('<li>')) {
|
|
if (content.match(/^\d+\./m)) {
|
|
html = '<ol>' + html + '</ol>';
|
|
} else {
|
|
html = '<ul>' + html + '</ul>';
|
|
}
|
|
}
|
|
|
|
// Convert line breaks to paragraphs for non-list content
|
|
if (!html.includes('<li>')) {
|
|
html = html.split('\n').filter(l => l.trim()).map(l => '<p>' + l + '</p>').join('');
|
|
}
|
|
|
|
return html;
|
|
}
|
|
|
|
function createField(label, content, className = '') {
|
|
if (!content) return '';
|
|
return `
|
|
<div class="field ${className}">
|
|
<label class="field-label">${label} <span class="edit-icon" onclick="toggleEdit(this)" title="Edit field">✎</span></label>
|
|
<div class="field-value ${content.includes('\n') ? 'multiline' : ''}">${formatContent(content)}</div>
|
|
<textarea class="field-textarea">${content}</textarea>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function toggleEdit(icon) {
|
|
const field = icon.closest('.field');
|
|
field.classList.toggle('editing');
|
|
if (field.classList.contains('editing')) {
|
|
field.querySelector('.field-textarea').focus();
|
|
}
|
|
}
|
|
|
|
const sections = parseMarkdown(rawContent);
|
|
const container = document.getElementById('form-content');
|
|
|
|
const fieldMap = [
|
|
{ key: 'Tipo de usuario', label: 'Tipo de Usuario', class: '' },
|
|
{ key: 'Donde empieza', label: 'Punto de Entrada', class: '' },
|
|
{ key: 'Que quiere hacer el usuario', label: 'Objetivo del Usuario', class: '' },
|
|
{ key: 'Pasos', label: 'Pasos', class: 'field-steps' },
|
|
{ key: 'Que deberia pasar', label: 'Resultado Esperado', class: '' },
|
|
{ key: 'Problemas comunes', label: 'Problemas Comunes', class: 'field-problems' },
|
|
{ key: 'Casos especiales', label: 'Casos Especiales', class: 'field-special' },
|
|
{ key: 'Flujos relacionados', label: 'Flujos Relacionados', class: '' },
|
|
{ key: 'Notas tecnicas', label: 'Notas Tecnicas', class: 'field-technical' },
|
|
];
|
|
|
|
let html = '';
|
|
for (const field of fieldMap) {
|
|
if (sections[field.key]) {
|
|
html += createField(field.label, sections[field.key], field.class);
|
|
}
|
|
}
|
|
container.innerHTML = html;
|
|
</script>
|
|
</body>
|
|
</html>
|