Skip to content

Section 7: Register Templates

In this section, we aim to enhance the functionality of our web application by enabling administrators to register new templates. We will achieve this through the following objectives:

Objectives:

  • Update Base Routing: Refine the base routing to direct administrators to a dedicated admin page (store-admin.html) for template management.
  • Modify Admin Page: Revise store-admin.html to include a new form that allows template registration.
  • Create Registration Endpoint: Implement a new endpoint (/templates/:id) to handle the registration of templates via POST requests.

1. Configuring the Base Path for Admin Access

To facilitate administrators in registering new templates, the base path (/) needs to be updated to redirect to the store-admin.html page.

We can update our base path / with

app.get("/", async (context) => {
  const type = getUserType(context);

  if (type == "unauth") {
    const loginPage = await fs.promises.readFile("public/login.html", "utf8");
    return context.html(loginPage);
  }

  if (type == "admin") {
    const storeadminPage = await fs.promises.readFile("public/store-admin.html", "utf8");
    return context.html(storeadminPage);
  }
  else {
    const storefrontPage = await fs.promises.readFile("public/store-front.html", "utf8");
    return context.html(storefrontPage);
  }
});

This modification checks the user type and serves the appropriate page.

2. Updating the Admin Page

To allow template registration, the store-admin.html must be updated to include a template registration form. Refer to the provided HTML snippet below and update it store-admin.html.

store-admin.html cotent
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Templates Gallery</title>
<style>
  .template-container {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
  }
  .template {
    border: 1px solid #ccc;
    padding: 10px;
    width: 200px;
  }
  .template img {
    width: 200px;
    height: auto;
  }
  .template h3 {
    margin: 0;
    font-size: 1.2em;
    color: #333;
  }
  .template button {
    margin-top: 10px;
    margin-right: 5px; /* Add space between buttons */
  }
  .error-message {
    color: red;
    margin-top: 10px;
  }
</style>
</head>
<body>
<div>
  <form id="register-form">
    <label for="template-id">Register Template:</label>
    <input type="text" id="template-id" name="template-id" required>
    <button type="submit">Submit</button>
  </form>
  <div id="error-message" class="error-message"></div>
</div>
<h2>Templates</h2>
<div id="templates" class="template-container">
  <!-- Templates will be loaded here -->
</div>
<h2>User Projects</h2>
<div id="projects" class="template-container">
  <!-- Templates will be loaded here -->
</div>

<script>
  document.getElementById('register-form').onsubmit = function(event) {
    event.preventDefault();
    const templateId = document.getElementById('template-id').value;
    registerTemplate(templateId);
  };

  function registerTemplate(id) {
    fetch(`/templates/${id}`, { method: 'POST' })
      .then(response => {
        if (!response.ok) {
            return response.text().then(text => {
              throw new Error(`Registration failed: ${text}`);
            });
        }
        return response.json();
      })
      .then(data => {
        loadTemplates(); // Reload all templates
      })
      .catch(error => {
        document.getElementById('error-message').textContent = error.message;
      });
  }

  function loadTemplates() {
    fetch('/templates')
      .then(response => response.json())
      .then(data => {
        const templatesDiv = document.getElementById('templates');
        templatesDiv.innerHTML = '';
        data.forEach(template => {
          const div = document.createElement('div');
          div.id = template.id;
          div.className = 'template';
          div.innerHTML = `
            <h3>${template.name}</h3>
            <img src="preview/template/${template.id}" alt="${template.name}">
            <br/>
            <button onclick="removeTemplate('${template.id}')">Remove</button>
          `;
          templatesDiv.appendChild(div);
        });
      })
      .catch(error => console.error('Error loading templates:', error));
  }

  function editTemplate(id) {
    // Replace with the actual logic to edit the template
    console.log('Editing template', id);
  }

  function removeTemplate(id) {
    fetch(`/templates/${id}`, { method: 'DELETE' })
      .then(response => {
        if (!response.ok) {
          throw new Error('Deletion failed');
        }
        loadTemplates(); // Reload all templates after deletion
      })
      .catch(error => {
        document.getElementById('error-message').textContent = error.message;
      });
  }

  window.onload = function() {
      // Initial load of templates
      loadTemplates();

      fetch('/user/projects')
      .then(response => response.json())
      .then(data => {
        const projectsDiv = document.getElementById('projects');

        data.forEach(project => {
          const div = document.createElement('div');
          div.id = project.id;
          div.className = 'template';
          div.innerHTML = `
            <h3>${project.name}</h3>
            <img src="preview/project/${project.id}" alt="${project.name}">
            <br/>
            <button onclick="window.location.href = '/editor/${project.id}'">Edit</button>
          `;
          projectsDiv.appendChild(div);
        });
      })
      .catch(error => console.error('Error loading templates:', error));
  };

</script>
</body>
</html>

This form will send a POST request to /templates/:id to register a new template.

3. Adding the Template Registration Endpoint

A new endpoint needs to be added to handle template registration. Update your server.js with the following route:

app.post("/templates/:id", async (context) => {
  const type = getUserType(context);

  if (type != "admin") {
    return context.text("Unauthorized", 403);
  }

  const {id} = context.req.param();
});

In the Hono framework, the param function simplifies the extraction of the id from the URL path.

Next, we need to integrate logic to add a template to the database using the addTemplate method.

app.post("/templates/:id", async (context) => {
  const type = getUserType(context);

  if (type != "admin") {
    return context.text("Unauthorized", 403);
  }

  const {id} = context.req.param();

  // db.addTemplate(???); - we only have id no name
});

However, the template's name is required and is not provided by the current implementation. Additionally, we must verify the template's existence in GraFx Studio to prevent registering non-existent templates.

To resolve these issues, we will interface with the Environment API, which will be covered in the next section, where we will also obtain the necessary token.