Selasa, 16 Desember 2025

Apps Script - Desain Form Pelatihan

 


Buatkan Form Pelatihan / Workshop dengan data disimpan pada google sheet. Back end menggunakan google apps script dan front end menggunakan index.html yang dipanggil dengan function doGet(e) {

return HtmlService.createHtmlOutputFromFile('index’)}. Untuk front end buatkan 


Landing Page (Hero Section)

  • Full screen
  • Gradient biru modern
  • Call-to-action “Mulai Isi Data”
  • Cocok untuk aplikasi GAS berbasis form publik

Form Dipisahkan Secara Visual

  • Landing page Form input
  • Navigasi tanpa reload (SPA-style sederhana)

UI Modern & Clean

  • Typography rapi
  • Card layout
  • Shadow + rounded corner
  • Warna biru konsisten (tema edukasi / sistem informasi)

Struktur Frontend Lebih Profesional

  • HTML semantic (section)
  • CSS terstruktur
  • JavaScript jelas dan minimal

<!doctype html>
<html lang="id">
 <head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Form Pelatihan Workshop</title>
  <script src="/_sdk/element_sdk.js"></script>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      line-height: 1.6;
      color: #333;
      box-sizing: border-box;
      overflow-x: hidden;
    }

    html, body, #app {
      height: 100%;
      width: 100%;
    }

    #app {
      overflow-auto;
    }

    /* Landing Page Styles */
    .landing-section {
      min-height: 100%;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 2rem;
      width: 100%;
    }

    .hero-content {
      text-align: center;
      color: white;
      max-width: 700px;
    }

    .hero-title {
      font-size: 3rem;
      font-weight: bold;
      margin-bottom: 1.5rem;
      text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
    }

    .hero-subtitle {
      font-size: 1.25rem;
      margin-bottom: 2.5rem;
      opacity: 0.95;
    }

    .cta-button {
      background: white;
      color: #667eea;
      padding: 1rem 3rem;
      font-size: 1.125rem;
      font-weight: 600;
      border: none;
      border-radius: 50px;
      cursor: pointer;
      transition: all 0.3s ease;
      box-shadow: 0 4px 15px rgba(0,0,0,0.2);
    }

    .cta-button:hover {
      transform: translateY(-2px);
      box-shadow: 0 6px 20px rgba(0,0,0,0.3);
    }

    /* Form Section Styles */
    .form-section {
      display: none;
      min-height: 100%;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      padding: 3rem 1.5rem;
      width: 100%;
    }

    .form-section.active {
      display: flex;
      flex-direction: column;
      align-items: center;
    }

    .form-container {
      background: white;
      border-radius: 20px;
      box-shadow: 0 10px 40px rgba(0,0,0,0.2);
      padding: 2.5rem;
      width: 100%;
      max-width: 600px;
      margin-bottom: 2rem;
    }

    .form-header {
      text-align: center;
      margin-bottom: 2rem;
    }

    .form-title {
      font-size: 2rem;
      color: #667eea;
      margin-bottom: 0.5rem;
    }

    .back-button {
      background: none;
      border: none;
      color: #667eea;
      cursor: pointer;
      font-size: 0.9rem;
      margin-bottom: 1rem;
      display: inline-flex;
      align-items: center;
      gap: 0.5rem;
      transition: color 0.3s ease;
    }

    .back-button:hover {
      color: #764ba2;
    }

    .form-group {
      margin-bottom: 1.5rem;
    }

    .form-label {
      display: block;
      margin-bottom: 0.5rem;
      font-weight: 600;
      color: #333;
    }

    .form-input,
    .form-select,
    .form-textarea {
      width: 100%;
      padding: 0.875rem;
      border: 2px solid #e0e0e0;
      border-radius: 10px;
      font-size: 1rem;
      transition: all 0.3s ease;
      font-family: inherit;
    }

    .form-input:focus,
    .form-select:focus,
    .form-textarea:focus {
      outline: none;
      border-color: #667eea;
      box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
    }

    .form-textarea {
      resize: vertical;
      min-height: 100px;
    }

    .submit-button {
      width: 100%;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
      padding: 1rem;
      font-size: 1.125rem;
      font-weight: 600;
      border: none;
      border-radius: 10px;
      cursor: pointer;
      transition: all 0.3s ease;
      margin-top: 1rem;
    }

    .submit-button:hover:not(:disabled) {
      transform: translateY(-2px);
      box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
    }

    .submit-button:disabled {
      opacity: 0.6;
      cursor: not-allowed;
    }

    .success-message,
    .error-message {
      text-align: center;
      padding: 1rem;
      border-radius: 10px;
      margin-top: 1rem;
      font-weight: 500;
    }

    .success-message {
      background: #d4edda;
      color: #155724;
      border: 1px solid #c3e6cb;
    }

    .error-message {
      background: #f8d7da;
      color: #721c24;
      border: 1px solid #f5c6cb;
    }

    .hidden {
      display: none;
    }

    @media (max-width: 768px) {
      .hero-title {
        font-size: 2rem;
      }

      .hero-subtitle {
        font-size: 1rem;
      }

      .form-container {
        padding: 1.5rem;
      }

      .form-title {
        font-size: 1.5rem;
      }
    }
  </style>
  <style>@view-transition { navigation: auto; }</style>
  <script src="/_sdk/data_sdk.js" type="text/javascript"></script>
  <script src="https://cdn.tailwindcss.com" type="text/javascript"></script>
 </head>
 <body>
  <div id="app"><!-- Landing Page -->
   <section class="landing-section" id="landingSection">
    <div class="hero-content">
     <h1 class="hero-title" id="heroTitle">Form Pendaftaran Pelatihan</h1>
     <p class="hero-subtitle" id="heroSubtitle">Daftarkan diri Anda untuk mengikuti pelatihan dan workshop profesional</p><button class="cta-button" id="ctaButton" onclick="showForm()">Mulai Isi Data</button>
    </div>
   </section><!-- Form Section -->
   <section class="form-section" id="formSection">
    <div class="form-container"><button class="back-button" onclick="showLanding()"> ← Kembali </button>
     <div class="form-header">
      <h2 class="form-title" id="formTitle">Data Peserta</h2>
     </div>
     <form id="registrationForm" onsubmit="handleSubmit(event)">
      <div class="form-group"><label class="form-label" for="nama">Nama Lengkap</label> <input type="text" id="nama" name="nama" class="form-input" required>
      </div>
      <div class="form-group"><label class="form-label" for="email">Email</label> <input type="email" id="email" name="email" class="form-input" required>
      </div>
      <div class="form-group"><label class="form-label" for="telepon">Nomor Telepon</label> <input type="tel" id="telepon" name="telepon" class="form-input" required>
      </div>
      <div class="form-group"><label class="form-label" for="instansi">Instansi/Perusahaan</label> <input type="text" id="instansi" name="instansi" class="form-input" required>
      </div>
      <div class="form-group"><label class="form-label" for="pelatihan">Pilih Pelatihan</label> <select id="pelatihan" name="pelatihan" class="form-select" required> <option value="">-- Pilih Pelatihan --</option> <option value="Web Development">Web Development</option> <option value="Data Science">Data Science</option> <option value="Digital Marketing">Digital Marketing</option> <option value="UI/UX Design">UI/UX Design</option> <option value="Project Management">Project Management</option> </select>
      </div>
      <div class="form-group"><label class="form-label" for="catatan">Catatan (Opsional)</label> <textarea id="catatan" name="catatan" class="form-textarea"></textarea>
      </div><button type="submit" class="submit-button" id="submitButton">Kirim Pendaftaran</button>
     </form>
     <div id="successMessage" class="success-message hidden"></div>
     <div id="errorMessage" class="error-message hidden"></div>
    </div>
   </section>
  </div>
  <script>
    const defaultConfig = {
      hero_title: "Form Pendaftaran Pelatihan",
      hero_subtitle: "Daftarkan diri Anda untuk mengikuti pelatihan dan workshop profesional",
      cta_button: "Mulai Isi Data",
      form_title: "Data Peserta",
      submit_button: "Kirim Pendaftaran",
      background_color: "#667eea",
      surface_color: "#ffffff",
      text_color: "#333333",
      primary_action_color: "#667eea",
      secondary_action_color: "#764ba2",
      font_family: "Segoe UI",
      font_size: 16
    };

    function showForm() {
      document.getElementById('landingSection').style.display = 'none';
      document.getElementById('formSection').classList.add('active');
    }

    function showLanding() {
      document.getElementById('formSection').classList.remove('active');
      document.getElementById('landingSection').style.display = 'flex';
    }

    function showMessage(elementId, message) {
      const element = document.getElementById(elementId);
      element.textContent = message;
      element.classList.remove('hidden');
      setTimeout(() => {
        element.classList.add('hidden');
      }, 5000);
    }

    function handleSubmit(event) {
      event.preventDefault();
      
      const submitButton = document.getElementById('submitButton');
      const form = document.getElementById('registrationForm');
      
      submitButton.disabled = true;
      submitButton.textContent = 'Mengirim...';

      const formData = {
        nama: document.getElementById('nama').value,
        email: document.getElementById('email').value,
        telepon: document.getElementById('telepon').value,
        instansi: document.getElementById('instansi').value,
        pelatihan: document.getElementById('pelatihan').value,
        catatan: document.getElementById('catatan').value,
        timestamp: new Date().toISOString()
      };

      // Untuk Google Apps Script deployment
      if (typeof google !== 'undefined' && google.script && google.script.run) {
        google.script.run
          .withSuccessHandler(function(response) {
            showMessage('successMessage', 'Pendaftaran berhasil dikirim! Terima kasih.');
            form.reset();
            submitButton.disabled = false;
            submitButton.textContent = window.elementSdk?.config?.submit_button || defaultConfig.submit_button;
          })
          .withFailureHandler(function(error) {
            showMessage('errorMessage', 'Terjadi kesalahan. Silakan coba lagi.');
            submitButton.disabled = false;
            submitButton.textContent = window.elementSdk?.config?.submit_button || defaultConfig.submit_button;
          })
          .submitForm(formData);
      } else {
        // Demo mode - simulasi sukses
        setTimeout(() => {
          showMessage('successMessage', 'Mode Demo: Data berhasil "dikirim"! (Dalam production, data akan tersimpan di Google Sheets)');
          form.reset();
          submitButton.disabled = false;
          submitButton.textContent = window.elementSdk?.config?.submit_button || defaultConfig.submit_button;
        }, 1000);
      }
    }

    async function onConfigChange(config) {
      const heroTitle = document.getElementById('heroTitle');
      const heroSubtitle = document.getElementById('heroSubtitle');
      const ctaButton = document.getElementById('ctaButton');
      const formTitle = document.getElementById('formTitle');
      const submitButton = document.getElementById('submitButton');

      if (heroTitle) heroTitle.textContent = config.hero_title || defaultConfig.hero_title;
      if (heroSubtitle) heroSubtitle.textContent = config.hero_subtitle || defaultConfig.hero_subtitle;
      if (ctaButton) ctaButton.textContent = config.cta_button || defaultConfig.cta_button;
      if (formTitle) formTitle.textContent = config.form_title || defaultConfig.form_title;
      if (submitButton && !submitButton.disabled) {
        submitButton.textContent = config.submit_button || defaultConfig.submit_button;
      }

      const baseSize = config.font_size || defaultConfig.font_size;
      const customFont = config.font_family || defaultConfig.font_family;
      const baseFontStack = 'Segoe UI, Tahoma, Geneva, Verdana, sans-serif';

      document.body.style.fontFamily = `${customFont}, ${baseFontStack}`;
      document.body.style.fontSize = `${baseSize}px`;

      if (heroTitle) heroTitle.style.fontSize = `${baseSize * 3}px`;
      if (heroSubtitle) heroSubtitle.style.fontSize = `${baseSize * 1.25}px`;
      if (ctaButton) ctaButton.style.fontSize = `${baseSize * 1.125}px`;
      if (formTitle) formTitle.style.fontSize = `${baseSize * 2}px`;
      if (submitButton) submitButton.style.fontSize = `${baseSize * 1.125}px`;

      const landingSection = document.getElementById('landingSection');
      const formSection = document.getElementById('formSection');
      const bgColor = config.background_color || defaultConfig.background_color;
      const secondaryColor = config.secondary_action_color || defaultConfig.secondary_action_color;
      
      if (landingSection) {
        landingSection.style.background = `linear-gradient(135deg, ${bgColor} 0%, ${secondaryColor} 100%)`;
      }
      if (formSection) {
        formSection.style.background = `linear-gradient(135deg, ${bgColor} 0%, ${secondaryColor} 100%)`;
      }

      const formContainer = document.querySelector('.form-container');
      if (formContainer) {
        formContainer.style.background = config.surface_color || defaultConfig.surface_color;
      }

      const primaryColor = config.primary_action_color || defaultConfig.primary_action_color;
      const textColor = config.text_color || defaultConfig.text_color;

      if (ctaButton) {
        ctaButton.style.color = primaryColor;
      }

      const backButton = document.querySelector('.back-button');
      if (backButton) {
        backButton.style.color = primaryColor;
      }

      if (formTitle) {
        formTitle.style.color = primaryColor;
      }

      if (submitButton) {
        submitButton.style.background = `linear-gradient(135deg, ${primaryColor} 0%, ${secondaryColor} 100%)`;
      }

      const labels = document.querySelectorAll('.form-label');
      labels.forEach(label => {
        label.style.color = textColor;
      });
    }

    if (window.elementSdk) {
      window.elementSdk.init({
        defaultConfig,
        onConfigChange,
        mapToCapabilities: (config) => ({
          recolorables: [
            {
              get: () => config.background_color || defaultConfig.background_color,
              set: (value) => {
                config.background_color = value;
                window.elementSdk.setConfig({ background_color: value });
              }
            },
            {
              get: () => config.surface_color || defaultConfig.surface_color,
              set: (value) => {
                config.surface_color = value;
                window.elementSdk.setConfig({ surface_color: value });
              }
            },
            {
              get: () => config.text_color || defaultConfig.text_color,
              set: (value) => {
                config.text_color = value;
                window.elementSdk.setConfig({ text_color: value });
              }
            },
            {
              get: () => config.primary_action_color || defaultConfig.primary_action_color,
              set: (value) => {
                config.primary_action_color = value;
                window.elementSdk.setConfig({ primary_action_color: value });
              }
            },
            {
              get: () => config.secondary_action_color || defaultConfig.secondary_action_color,
              set: (value) => {
                config.secondary_action_color = value;
                window.elementSdk.setConfig({ secondary_action_color: value });
              }
            }
          ],
          borderables: [],
          fontEditable: {
            get: () => config.font_family || defaultConfig.font_family,
            set: (value) => {
              config.font_family = value;
              window.elementSdk.setConfig({ font_family: value });
            }
          },
          fontSizeable: {
            get: () => config.font_size || defaultConfig.font_size,
            set: (value) => {
              config.font_size = value;
              window.elementSdk.setConfig({ font_size: value });
            }
          }
        }),
        mapToEditPanelValues: (config) => new Map([
          ["hero_title", config.hero_title || defaultConfig.hero_title],
          ["hero_subtitle", config.hero_subtitle || defaultConfig.hero_subtitle],
          ["cta_button", config.cta_button || defaultConfig.cta_button],
          ["form_title", config.form_title || defaultConfig.form_title],
          ["submit_button", config.submit_button || defaultConfig.submit_button]
        ])
      });
    }
  </script>
 <script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'9af267ad54b6d210',t:'MTc2NTkzMjQwMy4wMDAwMDA='};var a=document.createElement('script');a.nonce='';a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body>
</html>

Tidak ada komentar:

Posting Komentar

Sylabus Struktur Data Using CPP

  https://www.youtube.com/watch?v=PQrkEa5aK3I Minggu Topik Utama Materi Pembahasan 1 Review Dasar C++ & Memory Pointer, reference, alama...