survey-questionnaire.html #5

  • //
  • guest/
  • perforce_software/
  • p4survey/
  • docs/
  • survey-questionnaire.html
  • View
  • Commits
  • Open Download .zip Download (13 KB)
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>P4 Survey Questionnaire</title>
  <style>
    :root {
      color: #1f2933;
      background: #e8f0f2;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      line-height: 1.5;
    }

    * { box-sizing: border-box; }

    body { margin: 0; }

    main {
      width: min(100% - 2rem, 70rem);
      margin: 2rem auto;
      background: #fff;
      border-top: 5px solid #0d6b85;
      box-shadow: 0 0.75rem 2.5rem rgb(31 41 51 / 12%);
    }

    header {
      padding: 2rem 2.25rem 1.5rem;
      border-bottom: 1px solid #d6e1e6;
    }

    h1, h2 { color: #173f52; line-height: 1.2; }
    h1 { margin: 0; font-size: 2rem; }
    h2 { margin: 0 0 1rem; font-size: 1.25rem; }
    p { margin: 0.75rem 0 0; }

    .content {
      display: grid;
      grid-template-columns: minmax(0, 1.15fr) minmax(18rem, 0.85fr);
      gap: 2rem;
      padding: 2rem 2.25rem 2.25rem;
    }

    form { display: grid; gap: 1.5rem; }
    fieldset { margin: 0; padding: 1.25rem; border: 1px solid #c6d7dc; }
    legend { padding: 0 0.4rem; color: #173f52; font-weight: 700; }
    .fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; }
    .full { grid-column: 1 / -1; }

    label { display: grid; gap: 0.35rem; font-weight: 650; }
    input, select, textarea, button { font: inherit; }
    input, select, textarea {
      width: 100%;
      padding: 0.55rem 0.65rem;
      border: 1px solid #9eb7c0;
      border-radius: 3px;
      color: #1f2933;
      background: #fff;
    }
    input:focus, select:focus, textarea:focus { outline: 3px solid rgb(13 107 133 / 25%); border-color: #0d6b85; }
    textarea { min-height: 7rem; resize: vertical; }

    .options { display: grid; gap: 0.55rem; }
    .option { display: flex; align-items: center; gap: 0.55rem; font-weight: 400; }
    .option input { width: auto; margin: 0; }

    .actions { display: flex; flex-wrap: wrap; gap: 0.75rem; }
    button { padding: 0.65rem 1rem; border: 1px solid transparent; border-radius: 3px; cursor: pointer; font-weight: 700; }
    button[type="submit"] { color: #fff; background: #0d6b85; }
    button[type="submit"]:hover { background: #07546b; }
    button[type="reset"] { color: #173f52; background: #edf2f4; border-color: #9eb7c0; }

    aside { min-width: 0; }
    .preview {
      min-height: 24rem;
      margin: 0;
      padding: 1rem;
      overflow: auto;
      white-space: pre-wrap;
      border: 1px solid #c6d7dc;
      background: #f6fafb;
      color: #263a44;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
      font-size: 0.875rem;
      line-height: 1.45;
    }

    .note { color: #4d6572; font-size: 0.9rem; }

    @media (max-width: 760px) {
      main { width: 100%; margin: 0; box-shadow: none; }
      header, .content { padding: 1.5rem; }
      .content { grid-template-columns: 1fr; }
      .fields { grid-template-columns: 1fr; }
      .full { grid-column: auto; }
    }

    .content { align-items: start; }
    #questionnaire { gap: 1.25rem; }
    fieldset { min-inline-size: 0; }
    .question { display: grid; gap: 0.4rem; }
    .question + .question { margin-top: 1rem; }
    .question label { font-weight: 650; }
    .prompt { margin: 0; color: #263a44; }
    .helper { margin: 0; color: #4d6572; font-size: 0.9rem; }
    .question textarea { min-height: 6.5rem; }
    .question textarea.short { min-height: 4.25rem; }
    aside { position: sticky; top: 1rem; }
    .preview { min-height: 32rem; max-height: calc(100vh - 10rem); }
    @media (max-width: 760px) {
      aside { position: static; }
      .preview { max-height: none; }
    }
  </style>
</head>
<body>
  <main>
    <header>
      <h1>Pre-Consulting Services Site Survey</h1>
      <p><strong>Download this questionnaire and open the downloaded file in a browser before completing it.</strong> It requires JavaScript and cannot be completed when displayed in Swarm/Code Review.</p>
      <p>Capture the deployment context that accompanies a <code>p4survey</code> report. Answer what you can and return it promptly; responses stay in this browser and download as a Markdown file so that you can email it to us.</p>
      <p class="note"><a href="../README.html#Download">Back to the p4survey README where you can download the tool.</a></p>
    </header>

    <div class="content">
      <form id="questionnaire" novalidate>
        <div id="questions"></div>
        <div class="actions">
          <button type="submit">Download Markdown</button>
          <button type="reset">Clear form</button>
        </div>
      </form>

      <aside aria-label="Markdown preview">
        <h2>Markdown Preview</h2>
        <pre class="preview" id="preview"></pre>
        <p class="note">The preview updates as you type. Download uses your browser's local save location.</p>
      </aside>
    </div>
  </main>

  <script>
    (() => {
      const form = document.querySelector('#questionnaire');
      const preview = document.querySelector('#preview');

      const sections = [
        {
          title: 'Customer Details',
          questions: [
            { label: 'Customer company', name: 'company', type: 'text' },
            { label: 'Primary contact', helper: 'Email address, name, and job title.', name: 'contact1', type: 'text' }
          ]
        },
        {
          title: '1. Customer Perspective',
          questions: [
            { label: 'What prompted this survey?', name: 'purpose', type: 'select', options: ['Routine health review', 'Performance investigation', 'Upgrade or migration planning', 'Support request', 'Other'] },
            { label: 'What should the review focus on?', name: 'focus', type: 'checkboxes', options: ['Performance and capacity', 'High availability and recovery', 'Security and access controls', 'Upgrade or migration readiness'] },
            { label: '1.1 Issues Reported', helper: 'What are the main issues Perforce users and administrators report? If possible include impact, frequency, and examples.', name: 'issues', type: 'textarea' },
            { label: '1.2 Goals Identified', helper: 'What goals support continued Perforce growth and usability?', name: 'goals', type: 'textarea' },
            { label: 'Current symptoms, risks, or questions', helper: 'Describe observed issues, timing, impact, or specific questions.', name: 'concerns', type: 'textarea' },
            { label: 'Planned changes or relevant dates', helper: 'Include planned upgrades, maintenance windows, growth expectations, or deadlines.', name: 'changes', type: 'textarea' }
          ]
        },
        {
          title: '2. Perforce Architecture',
          questions: [
            { label: '2.1 User Locations', helper: 'Where are users geo-located relative to the Perforce servers? Include approximate user totals by location.', name: 'userLocations', type: 'textarea', short: true }
          ]
        },
        {
          title: '3. Perforce Server Overview',
          questions: [
            { label: '3.1 Hardware or Operating System Issues', helper: 'Note particular or recent hardware and operating-system problems.', name: 'serverIssues', type: 'textarea', short: true },
            { label: '3.2 Server Monitoring', helper: 'What tool(s) are used for server monitoring? Include a weekly CPU screenshot if available and user perception of performance.', name: 'monitoring', type: 'textarea' },
            { label: '3.3 Server Access', helper: 'How do users and administrators access the server: LAN, VPN, or whitelisted IP addresses?', name: 'serverAccess', type: 'textarea', short: true },
            { label: '3.4 Recent Outages', helper: 'How many unplanned outages or significant service disruptions occurred in the last week and last month? Briefly describe the cause and impact.', name: 'recentOutages', type: 'textarea', short: true }
          ]
        },
        {
          title: '4. Supporting Software and Internal Tools',
          questions: [
            { label: '4.1 Supporting Software, Internal Tools, and Issues', helper: 'Summarise backup, defect tracking, build tools, anti-virus, in-house tools. Note any associated issues or Perforce interactions.', name: 'supportingTools', type: 'textarea' }
          ]
        },
        {
          title: '5. Perforce Administration Practices',
          questions: [
            { label: '5.1 Automated Backups', helper: 'How often are backups taken, and where are they stored? Are backup success and restore capability checked?', name: 'automatedBackups', type: 'textarea', short: true },
            { label: '5.2 Redundant Data Storage', helper: 'Describe off-site data storage and transfer frequency.', name: 'offsiteStorage', type: 'textarea', short: true },
            { label: '5.3 Failover', helper: 'Is failover available? Briefly describe the process and when it was last tested.', name: 'failover', type: 'textarea', short: true },
            { label: '5.4 Disaster Recovery', helper: 'Is there a tested disaster-recovery plan? Include the expected recovery time and acceptable data loss, if known.', name: 'disasterRecovery', type: 'textarea', short: true },
            { label: '5.5 Perforce Upgrades', helper: 'How often is Perforce upgraded, and how are upgrades tested before production?', name: 'upgrades', type: 'textarea', short: true }
          ]
        },
        {
          title: '6. Miscellaneous',
          questions: [
            { label: '6.1 Additional Items', helper: 'Add any useful details not addressed above.', name: 'additionalItems', type: 'textarea' }
          ]
        }
      ];

      const questions = document.querySelector('#questions');
      const escapeHtml = text => text.replace(/[&<>"']/g, character => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' })[character]);
      const field = question => {
        const helper = question.helper ? `<p class="helper">${escapeHtml(question.helper)}</p>` : '';
        let control;
        if (question.type === 'textarea') {
          control = `<textarea id="${question.name}" name="${question.name}"${question.short ? ' class="short"' : ''}></textarea>`;
        } else if (question.type === 'select') {
          control = `<select id="${question.name}" name="${question.name}">${question.options.map(option => `<option>${escapeHtml(option)}</option>`).join('')}</select>`;
        } else if (question.type === 'checkboxes') {
          control = `<div class="options">${question.options.map(option => `<label class="option"><input name="${question.name}" type="checkbox" value="${escapeHtml(option)}">${escapeHtml(option)}</label>`).join('')}</div>`;
        } else {
          control = `<input id="${question.name}" name="${question.name}" type="text">`;
        }
        return `<div class="question"><label for="${question.name}">${escapeHtml(question.label)}</label>${helper}${control}</div>`;
      };
      questions.innerHTML = sections.map(section => `<fieldset><legend>${escapeHtml(section.title)}</legend>${section.questions.map(field).join('')}</fieldset>`).join('');

      const value = name => form.elements[name].value.trim().replace(/\r\n/g, '\n');
      const response = question => question.type === 'checkboxes'
        ? [...form.querySelectorAll(`input[name="${question.name}"]:checked`)].map(input => `- ${input.value}`).join('\n')
        : value(question.name);

      function output() {
        const lines = [
          '# Perforce Consulting Services Site Survey',
          '',
          `- Completed: ${new Date().toISOString().slice(0, 10)}`,
          ''
        ];
        sections.forEach(section => {
          lines.push(`## ${section.title}`, '');
          section.questions.forEach(question => {
            lines.push(`### ${question.label}`, '', response(question) || '[not provided]', '');
          });
        });
        return lines.join('\n');
      }

      function updatePreview() {
        preview.textContent = output();
      }

      form.addEventListener('input', updatePreview);
      form.addEventListener('change', updatePreview);
      form.addEventListener('reset', () => requestAnimationFrame(updatePreview));
      form.addEventListener('submit', event => {
        event.preventDefault();
        const blob = new Blob([output()], { type: 'text/markdown;charset=utf-8' });
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        const filenamePart = (value('company') || 'perforce').replace(/[^a-z0-9]+/gi, '-').replace(/^-|-$/g, '').toLowerCase();
        link.download = `${filenamePart || 'perforce'}-site-survey.md`;
        link.hidden = true;
        document.body.append(link);
        link.click();
        setTimeout(() => {
          URL.revokeObjectURL(link.href);
          link.remove();
        }, 0);
      });

      updatePreview();
    })();
  </script>
</body>
</html>
# Change User Description Committed
#5 34015 Robert Cowham Update questionnaire to say it needs to be downloaded and completed offline
#4 33967 Robert Cowham Tweak text
#3 33965 Robert Cowham Cut down version
#2 33964 Robert Cowham Trying out full doc before editing down
#1 33963 Robert Cowham Playing with questionnaire