update dependencies and devDependencies, address lint issues
[squeep-indie-auther] / src / template / admin-html.js
index 085b95a0ed083eaf4fafa4c131a0496664d37f93..f72102f27f8bf09883b930b2eeed054d4f5f6735 100644 (file)
@@ -7,51 +7,82 @@
  */
 
 const th = require('./template-helper');
+const { sessionNavLinks } = require('@squeep/authentication-module');
 
 
+/**
+ *
+ * @param {string} profile profile
+ * @returns {string} li
+ */
 function renderProfileLI(profile) {
-  return `\t<li><a class="uri" name="${profile}">${profile}</a></li>`;
+  return `\t<li><a class="uri" id="${profile}">${profile}</a></li>`;
 }
 
 
+/**
+ *
+ * @param {string} profile profile
+ * @param {string} scope scope
+ * @param {boolean} selected is selected
+ * @returns {string} td
+ */
 function renderProfileScopeIndicator(profile, scope, selected) {
   const checked = selected ? ' checked' : '';
   return `\t\t<td>
-\t\t\t<input type="checkbox" id="${profile}-${scope}" name="scopes-${profile}" value="${scope}"${checked}>
+\t\t\t<input type="checkbox" id="${profile}-${scope}" name="scopes-${profile}[]" value="${scope}"${checked}>
 \t\t</td>`;
 }
 
+/**
+ *
+ * @param {string} scope scope
+ * @param {object} details details
+ * @param {string[]} profiles profiles
+ * @returns {string} tr
+ */
 function renderScopeRow(scope, details, profiles) {
   return `\t<tr class="scope">
 ${(profiles || []).map((profile) => renderProfileScopeIndicator(profile, scope, details.profiles.includes(profile))).join('\n')}
-\t\t<th scope="row"><label>${scope}<label></th>
+\t\t<th scope="row"><label>${scope}</label></th>
 \t\t<td class="description">${details.description}</td>
 \t\t<td>${details.application}</td>
 \t\t<td class="scope-actions">` +
     (details.isManuallyAdded ? `
-\t\t\t<button name="action" value="delete-scope-${encodeURIComponent(scope)}">Delete</button>
+\t\t\t<button type="submit" name="action" value="delete-scope-${encodeURIComponent(scope)}">Delete</button>
 ` : '') + `
 \t\t</td>
 \t</tr>`;
 }
 
 
+/**
+ *
+ * @param {string} profile profile
+ * @returns {string} th
+ */
 function renderProfileHeader(profile) {
-  return `<th class="vertical uri">
+  return `<th scope="col" class="vertical uri">
 \t\t${profile}
 </th>`;
 }
 
 
+/**
+ *
+ * @param {object} scopeIndex scopes
+ * @param {string[]} profiles profiles
+ * @returns {string} table
+ */
 function scopeIndexTable(scopeIndex, profiles) {
   return `<table>
 <thead>
 \t<tr>
 ${(profiles || []).map((profile) => renderProfileHeader(profile)).join('\n')}
-\t\t<th>Scope</th>
-\t\t<th>Description</th>
-\t\t<th>Application</th>
-\t\t<th class="scope-actions"></th>
+\t\t<th scope="col">Scope</th>
+\t\t<th scope="col">Description</th>
+\t\t<th scope="col">Application</th>
+\t\t<th scope="col" class="scope-actions"></th>
 \t</tr>
 </thead>
 <tbody>
@@ -60,6 +91,11 @@ ${Object.entries(scopeIndex).sort(th.scopeCompare).map(([scope, details]) => ren
 </table>`;
 }
 
+/**
+ *
+ * @param {object} token token
+ * @returns {string} type
+ */
 function _tokenType(token) {
   if (token.resource) {
     return 'ticket-token';
@@ -70,6 +106,11 @@ function _tokenType(token) {
   return 'token';
 }
 
+/**
+ *
+ * @param {object} token token
+ * @returns {string} tr
+ */
 function renderTokenRow(token) {
   const createdTitle = token.refreshed ? 'Refreshed At' : 'Created At';
   const createdDate = token.refreshed ? token.refreshed : token.created;
@@ -85,47 +126,63 @@ function renderTokenRow(token) {
 <td>${token.resource ? token.resource : ''}</td>
 \t\t\t<td>` + (
     token.isRevoked ? '' : `
-\t\t\t\t<button name="action" value="revoke-${token.codeId}">Revoke</button>`) + `
+\t\t\t\t<button type="submit" name="action" value="revoke-${token.codeId}">Revoke</button>`) + `
 \t\t\t</td>
 \t\t</tr>`;
 }
 
+/**
+ * @returns {string} tr
+ */
 function noTokensRows() {
   return [`\t\t<tr>
-\t\t\t<td colspan="100%" class="centered">(No active or recent tokens.)</td>
+\t\t\t<td colspan="10" class="centered">(No active or recent tokens.)</td>
 \t\t</tr>`];
 }
 
+/**
+ *
+ * @param {object} tokens tokens
+ * @returns {string} table
+ */
 function tokenTable(tokens) {
   const tokenRows = tokens?.length ? tokens.map((token) => renderTokenRow(token)) : noTokensRows();
-  return `<table>
+  const formOpen = tokens?.length ? '<form method="POST">\n' : '';
+  const formClose = tokens?.length ? '\n</form>' : '';
+  return `${formOpen}<table>
 \t<thead>
 \t\t<tr>
-<th>Type</th>
-\t\t\t<th>Client Identifier</th>
-\t\t\t<th>Profile</th>
-<th>Scopes</th>
-\t\t\t<th>Code</th>
-\t\t\t<th>Created or Refreshed</th>
-\t\t\t<th>Expires</th>
-\t\t\t<th>Revoked</th>
-<th>Resource</th>
-\t\t\t<th></th>
+\t\t\t<th scope="col">Type</th>
+\t\t\t<th scope="col">Client Identifier / Ticket Subject</th>
+\t\t\t<th scope="col">Profile</th>
+\t\t\t<th scope="col">Scopes</th>
+\t\t\t<th scope="col">Code</th>
+\t\t\t<th scope="col">Created or Refreshed</th>
+\t\t\t<th scope="col">Expires</th>
+\t\t\t<th scope="col">Revoked</th>
+\t\t\t<th scope="col">Resource</th>
+\t\t\t<th scope="col"></th>
 \t\t</tr>
 \t</thead>
 \t<tbody>
 ${tokenRows.join('\n')}
 \t</tbody>
-</table>`;
+</table>${formClose}`;
 }
 
+/**
+ *
+ * @param {object} ctx context
+ * @returns {string} section
+ */
 function mainContent(ctx) {
+  const profileList = (ctx.profilesScopes?.profiles || []).map((p) => renderProfileLI(p)).join('\n');
   return `<section>
 \t<h2>Profiles</h2>
 \t<ul>
-\t${(ctx.profilesScopes?.profiles || []).map((p) => renderProfileLI(p)).join('\n')}
+${profileList}
 \t</ul>
-\t<form action="" method="POST">
+\t<form method="POST">
 \t\t<fieldset>
 \t\t\t<legend>Add New Profile</legend>
 \t\t\t<div>
@@ -135,17 +192,17 @@ function mainContent(ctx) {
 \t\t\t<br>
 \t\t\t<label for="profile">Profile URL:</label>
 \t\t\t<input type="url" id="profile" name="profile" size="96">
-\t\t\t<button name="action" value="new-profile">Add Profile</button>
+\t\t\t<button type="submit" name="action" value="new-profile">Add Profile</button>
 \t\t</fieldset>
 \t</form>
 </section>
 <section>
 \t<h2>Scopes</h2>
-\t<form action="" method="POST">
 \t\t<details>
-\t\t<summary>
-\t\tScopes Associated with Profiles for Convenience
-\t\t</summary>
+\t\t\t<summary>
+\t\t\t\tScopes Associated with Profiles for Convenience
+\t\t\t</summary>
+\t\t<form method="POST">
 \t\t\t<fieldset>
 \t\t\t\t<legend>Manage Additional Profile Scope Availability</legend>
 \t\t\t\t<div>
@@ -154,12 +211,12 @@ function mainContent(ctx) {
 \t\t\t\t\tAny scope not in this table or not selected for a profile can always be added in the ad hoc field on the authorization request.
 \t\t\t\t</div>
 \t\t\t\t<br>
-\t\t${scopeIndexTable(ctx.profilesScopes.scopeIndex, ctx.profilesScopes.profiles)}
-\t\t\t\t<button name="action" value="save-scopes">Save</button>
+${scopeIndexTable(ctx.profilesScopes.scopeIndex, ctx.profilesScopes.profiles)}
+\t\t\t\t<button type="submit" name="action" value="save-scopes">Save</button>
 \t\t\t</fieldset>
 \t\t</form>
 \t\t<br>
-\t\t<form action="" method="POST">
+\t\t<form method="POST">
 \t\t\t<fieldset>
 \t\t\t\t<legend>Add New Scope</legend>
 \t\t\t\t<label for="scope">Scope:</label>
@@ -168,45 +225,41 @@ function mainContent(ctx) {
 \t\t\t\t<input type="text" id="description" name="description">
 \t\t\t\t<label for="application">Application:</label>
 \t\t\t\t<input type="text" id="application" name="application">
-\t\t\t\t<button name="action" value="new-scope">Add Scope</button>
+\t\t\t\t<button type="submit" name="action" value="new-scope">Add Scope</button>
 \t\t\t</fieldset>
+\t\t</form>
 \t\t</details>
-\t</form>
 </section>
 <section>
 \t<h2>Tokens</h2>
-\t<form action="" method="POST">
 ${tokenTable(ctx.tokens)}
-\t</form>
 </section>`;
 }
 
 
 /**
  * 
- * @param {Object} ctx
- * @param {Object} ctx.profilesScopes.scopeIndex
- * @param {String[]} ctx.profilesScopes.profiles
- * @param {Object[]} ctx.tokens
- * @param {Object} options
- * @param {Object} options.manager
- * @param {String} options.manager.pageTitle
- * @param {String} options.manager.logoUrl
- * @param {String[]} options.manager.footerEntries
- * @returns {String}
+ * @param {object} ctx context
+ * @param {object} ctx.profilesScopes.scopeIndex scopes
+ * @param {string[]} ctx.profilesScopes.profiles profiles
+ * @param {object[]} ctx.tokens tokens
+ * @param {object} options options
+ * @param {object} options.manager manager options
+ * @param {string} options.manager.pageTitle page title
+ * @param {string} options.manager.logoUrl logo url
+ * @param {string[]} options.manager.footerEntries footer entries
+ * @returns {string} page
  */
 module.exports = (ctx, options) => {
+  const pagePathLevel = 1;
   const htmlOptions = {
-    pageTitle: options.manager.pageTitle,
+    pageIdentifier: 'admin',
+    pageTitle: options.manager.pageTitle + ' - Admin',
     logoUrl: options.manager.logoUrl,
     footerEntries: options.manager.footerEntries,
-    navLinks: [
-      {
-        text: 'Ticket',
-        href: 'ticket',
-      },
-    ],
   };
+  th.navLinks(pagePathLevel, ctx, htmlOptions);
+  sessionNavLinks(1, ctx, htmlOptions);
   const content = [
     mainContent(ctx),
   ];