開発情報・ナレッジ

投稿者:SPIRERS ナレッジ向上チーム 2022年7月29日 (金)

お問い合わせ管理アプリ サイト設計・構築

チャットのような形式でやり取り可能なお問い合わせ管理デモを作成しました。
アプリ側でもやり取りの履歴が確認でき、相互コミュケーションがとれるアプリケーションです。
この記事は サイト設計・構築 のフェーズとなります。
関連記事はこちら
SPIRAL ver.2はプログラミング経験がなくても、オリジナルの業務アプリの制作・カスタマイズができるローコード開発プラットフォームです。
詳しくは SPIRAL ver.2 とはをご覧ください。

サイト機能

SPIRAL ver.2 のサイト管理機能では、静的なWebページを作成したり、
データベースに対して登録・更新するフォーム や データベースの情報を一覧形式で表示させるページなどを作成できます。
詳細は、サイト機能の全体像を確認してください。
今回は、サイト機能を使って、「お問い合わせアプリ」を作成します。

注意

設定の説明部分にて、ソースコードを記載しております。
ソースコードに記載されている ブロックのID や 各種リンク や name値 含めて文言などは、デモの環境に合わせたものとなります。
実際に同様のものを作成する際は、変更が必要となる箇所がありますので、ご注意ください。

フロー・使用機能の整理

お問い合わせ管理アプリの全体フローを整理し、認証エリア内で触れる画面と使用している機能をまとめました。
全体のフロー
▼使用する機能
認証エリア お問い合わせ回答チャット画面のクリック認証
ページ機能 お問い合わせページ
ブロック機能 登録フォーム
お問い合わせ登録・コメント登録 で使用しています
各機能の詳細に関しては、下記を参照ください。
ページ機能
ブロック機能
認証エリア

完成イメージ - お問い合わせ画面
完成イメージ - 回答チャット画面

お問い合わせページと回答チャットページの作成

手順1:お問い合わせページを作成
お問い合わせページを作成するため、ページの「+」より作成を行います。
識別名がURLのディレクトリ名になるので、使用したいURLの階層に合わせて設定してください。
手順2:お問い合わせ登録ブロックの作成
手順1で作成したページの BODY から「+」ボタンを押下し、登録ブロックを作成します。
ビジュアル設定にて、必要な項目の設定を行ってください。 回答チャット画面に 認証エリアの作成を行う必要があります。
認証エリアのIDは入力必須かつユニークのテキストフィールドやメールアドレスフィールドのみ設定可能です。
レコードIDは認証用のIDに使用できないため注意してください。
そのため PHP でランダムキーを発番し、hidden に値渡す処理を入れています。
ページにPHP と 登録ブロックに hidden セット用のHTMLを設置してください。

PHP
<?php
$SPIRAL->setTHValue("randomKey", makeRandStr(32));

function makeRandStr($length) {
    $str = array_merge(range('a', 'z'), range('0', '9'), range('A', 'Z'));
    $r_str = null;
    for ($i = 0; $i < $length; $i++) {
        $r_str .= $str[rand(0, count($str) - 1)];
    }
    return $r_str;
}
HTML
<!--/* 認証用ランダムキー(randomKey) */-->
<sp:input-field name="f0xx"></sp:input-field>
<input type="hidden" th:name="${fields['f0xx'].name}" th:value="${cp.result.value['randomKey']}">
今回も、コピペCSSを導入しています。
使用しているコピペCSSは、テーブルデザインになります。
手順3:ヘッダ・フッタ用ブロックを作成
手順1で作成したページの BODY から「+」ボタンを押下し、フリーコンテンツブロックで共通となるヘッダを作成します。
すぐに使える!ヘッダ&フッター テンプレートを元に作成しています。
フッタも同様の手順で作成してください。
ヘッダ・フッタの作成が完了しましたら、お問い合わせページの作成が完了となります。
手順4:お問い合わせ回答チャット画面の認証ページを作成
お問い合わせ回答チャット画面のページを作成するため、認証エリアのタブより認証エリアの作成を行います。
識別名がURLのディレクトリ名になるので、使用したいURLの階層に合わせて設定してください。
認証エリアの中でも認証が不要なページも作成できます。
今回は、認証エリア内にメール認証のページを作成するため、クリック認証にて設定いたします。
作成後、今回使用しない更新ページや削除ページは削除いただいても問題ありません。
手順5:お問い合わせ詳細を表示させるブロックの作成
クリックログインページ内にお問い合わせ詳細を表示させます。
ページに直接記載する方法でも問題ありませんが、今回はフリーコンテンツブロックにて設定します。
手順4で作成したページの BODY から「+」ボタンを押下し、フリーコンテンツブロックを作成します。
設定方法は、ソース設定を選択します。

HTML
<div class="sp-form-item sp-form-html" th:inline="none"><p><span style="font-size: 18pt;">お問い合わせ</span></p></div> 
<!--/* お問い合わせID */--> 
<div class="sp-form-item sp-form-field"> 
  <div class="sp-form-label" th:text="お問い合わせID">
    Label
  </div> 
  <div class="sp-form-data">
    <span class="sp-record-item-embedded" th:text="${siteClient.record['_id']}">12345</span>
  </div> 
</div> 
<!--/* ステータス */--> 
<div class="sp-form-item sp-form-field"> 
  <div class="sp-form-label" th:text="ステータス">
    Label
  </div> 
  <div class="sp-form-data">
    <span th:text="${siteClient.record[xx]?.label}">ラベル1</span>
  </div> 
</div> 
<!--/* お名前(name) */--> 
<div class="sp-form-item sp-form-field"> 
  <div class="sp-form-label" th:text="お名前">
    Label
  </div> 
  <div class="sp-form-data">
    <span th:text="${siteClient.record[xx]}">Text</span>
  </div> 
</div> 
<!--/* メールアドレス(mail) */--> 
<div class="sp-form-item sp-form-field"> 
  <div class="sp-form-label" th:text="メールアドレス">
    Label
  </div> 
  <div class="sp-form-data">
    <span th:text="${siteClient.record[xx]}">spiral@example.com</span>
  </div> 
</div> 
<!--/* お問い合わせ種別(contactType) */--> 
<div class="sp-form-item sp-form-field"> 
  <div class="sp-form-label" th:text="お問い合わせ種別">
    Label
  </div> 
  <div class="sp-form-data">
    <span th:text="${siteClient.record[xx]?.label}">ラベル1</span>
  </div> 
</div> 
<!--/* お問い合わせ内容(contactText) */--> 
<div class="sp-form-item sp-form-field"> 
  <div class="sp-form-label" th:text="お問い合わせ内容">
    Label
  </div> 
  <div class="sp-form-data">
    <span class="sp-form-embedded"> 
      <th:block th:each="line, stat : ${siteClient.record[7]?.lines}">
        <th:block th:text="${line}"/>
        <br th:unless="${stat.last}">
      </th:block>
    </span>
  </div> 
</div> 
<!--/* 回答希望日(answerDate) */--> 
<div class="sp-form-item sp-form-field"> 
  <div class="sp-form-label" th:text="回答希望日">
    Label
  </div> 
  <div class="sp-form-data">
    <span th:text="${siteClient.record[xx]}">2000-01-01</span>
  </div> 
</div>

詳細画面のデザインは、お問い合わせ登録の確認ページのデザインを使用しています。
登録ブロックの確認ステップより、HTMLを取得し、表示部分を認証エリアように書き換えています。
手順6:回答履歴を表示するレコードリストブロックの作成
お問い合わせコメントDBに登録されている回答履歴を表示するレコードリストを作成します。
お問い合わせに紐づいた情報を絞り込んで表示させるためには、レコード公開範囲を設定する必要があります。
エリア認証時公開のみ表示を行い、ログインフィルタにてお問い合わせマスタと連携で設定が必要です。
レコード公開範囲の設定が完了しましたら、回答履歴となるレコードリストを作成を作成します。
回答履歴では検索機能を使用しないので、レコードリストブロックを選択してください。
コメント風に表示させるため下記のようにHTMLおよびCSSを標準ソースより修正しています。

HTML
<div class="sp-record-list-no-records" th:if="${recordList.totalRecordCount ne 0}">
    <hr />
    <div class="sp-record-list-parts sp-html-parts"><p><span style="font-size: 1.5rem;">コメント履歴 - お問い合わせデモ</span></p></div> 
    <th:block th:each="record, stat : ${pageRecords}">
      <sp:record-data-field name="f0xx"></sp:record-data-field>
      <sp:record-data-field name="f0xx"></sp:record-data-field> 
      <sp:record-data-field name="f0xx"></sp:record-data-field>
        <div th:class="${record['f0xx'].id eq 1} ? 'comment-set-box right' : 'comment-set-box lecf'">
          <div class="icon-box">
            <th:block th:if="${record['f0xx'].id eq 1}">
                <span class="iconTxt" th:text="${siteClient.record[2]}">Text</span>
                <p class="iconName" th:text="${siteClient.record[2]}">Text</p>
            </th:block>
            <th:block th:if="${record['f0xx'].id eq 2}">
                <img src="/_media/contact/demoIcon.png" width="40" height="40" class="icon">
                <p class="iconName">担当者</p>
            </th:block>
          </div>
          <div class="comment">
              <th:block th:each="line, stat : ${record['f0xx']?.lines}">
                <th:block th:text="${line}" />
                <br th:unless="${stat.last}">
              </th:block>
              <!--/* ファイル (file) */-->
              <th:block th:with="files=${record['f0xx']}" th:remove="${files == null ? 'body' : 'none'}">
                <th:block th:if="${files.size == 1}" th:with="file=${files[0]}">
                  <div class="fileTxt">
                    <a class="sp-form-file-bold-link" th:title="|${file.fileName} (${file.fileSize})|" th:text="${file.fileName}" th:href="${file.fileUrl}"></a>
                    <span class="sp-form-file-text-label" th:text="|(${file.fileSize})|">Example</span>
                  </div>
                </th:block>
                <span class="sp-form-file-text-label" th:if="${files.size gt 1}" th:text="|${files.size} files|">Example</span>
              </th:block>
          </div>
        </div>
        <p  th:class="${record['f0xx'].id eq 1} ? 'timeStampR' : 'timeStampL'" th:text="${record['f_created_at'] != null} ? ${record['f_created_at'].format('yyyy/MM/dd HH:mm:ss')} : ''">2000/01/01 12:00:00</p>
    </th:block>
  </div>
CSS
.comment-set-box {
    display: flex;
    flex-wrap: wrap;
}

.comment-set-box.left {
    flex-direction: row;
}
.comment-set-box.right {
    flex-direction: row-reverse;
}

.comment {
    position: relative;
    display: inline-block;
    max-width: 80%;
    margin: 25px 20px 5px 20px;
    padding: 8px 15px;
    background: #f0f0f0;
    text-align: left;
    border-radius: 15px;
    word-wrap: break-word;
    max-height: initial;
    height: fit-content;
}
@media screen and (max-width: 959px) {
    .comment {
	    max-width: 75%;
    }
}
@media screen and (max-width: 480px) {
	.comment {
	    max-width: 55%;
    }
}


.comment::after {
    content: '';
    border: 14px solid transparent;
    border-top-color: #f0f0f0;
    position: absolute;
    top: 0;
}

.left .comment::after {
    left: -10px;
}

.right .comment::after {
    right: -10px;
}

.icon-box img {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 2px solid #fff;
    box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
    overflow: hidden;
}

.icon-box span.iconTxt {
    display: block;
    height: 55px;
    width: 55px;
    line-height: 60px;
    text-align: center;
    background: #ef4040;
    border-radius: 100%;
    font-size: 0;
    color: #FFF;
    font-weight: bold;
}

.icon-box span.iconTxt:first-letter {
    font-size: 30px;
}

.icon-box p {
    margin: 0;
    text-align: center;
    font-size: 0.75em;
}

.timeStampL {
    margin-left: 75px;
    font-size: 0.75em;
    margin-bottom: 1.2em;
    margin-top: 2px;
}

.timeStampR {
    text-align: right;
    margin-right: 85px;
    font-size: 0.75em;
    margin-bottom: 1.2em;
    margin-top: 2px;
}
.fileTxt{
    padding-top: 30px;
}
.fileTxt a{
    transition: color 0.3s;
    color:#065FD4;
}
.fileTxt a.fileLink:hover{
    opacity: 0.6;
}
.fileTxt span{
    font-size: 0.7em;
}

手順7:コメント登録ブロック
再質問ができるようにコメント登録用のブロックを作成します。
こちらもコピペCSSを入れるため、ビジュアル設定にて、必要な項目の設定後、ソース設定に切り替えてください。

手順8:回答チャットページにPHP設定
登録・更新フォームからは、参照フィールドのデータを登録・更新できません。(ver.2.20時点)
コメント登録時にAPI にて参照フィールドを更新する処理をページ側に設定します。

PHP
<?php
//------------------------------
// 設定値用モジュール
//------------------------------
define("API_URL", "https://api.spiral-platform.com/v1");
define("API_KEY", $SPIRAL->getEnvValue("API_KEY"));
define("API_ROLE_ID",""); // ロールによるAPI権限が不要の場合

//------------------------------
// 設定値用モジュール
//------------------------------
$commonBase = CommonBase::getInstance();
// ステップのセット
$formComplete = $SPIRAL->getRegistrationForm("フォームブロックのID");
$SPIRAL->setTHValue("step", $formComplete->getStep());

// 完了画面での処理
if($formComplete->isCompletedStep()){    
    $record = $SPIRAL->getRecordValue();
    $dataPATCH = array(
      'contactID' => $SPIRAL->getAuthRecordByFieldId('_id'),
    );
    $appInsert = $commonBase->apiCurlAction("PATCH","/apps/".$SPIRAL->getEnvValue("APP_CONTACT_ID")."/dbs/".$SPIRAL->getEnvValue("DB_COMMENT_ID")."/records/".$record['item']['_id'],$dataPATCH);
    if(isset($appInsert['item'])){
      $SPIRAL->setTHValue("insert", true);
    }else{
      $SPIRAL->setTHValue("insert", "error");
      $SPIRAL->setTHValue("errorText", "登録に失敗しました。再度おためしください。");
    //$SPIRAL->setTHValue("errorText", print_r($appInsert,true));
    }
}


//------------------------------
// 共通用モジュール
//------------------------------

class CommonBase
{
	/**
	 * シングルトンインスタンス
	 * @var UserManager
	 */
	protected static $singleton;
    

	public function __construct()
	{
		if (self::$singleton) {
			throw new Exception('must be singleton');
		}
		self::$singleton = $this;
	}

	/**
	 * シングルトンインスタンスを返す
	 * @return UserManager
	 */
	public static function getInstance()
	{
		if (!self::$singleton) {
			return new CommonBase();
		} else {
			return self::$singleton;
		}
	}

    
	/**
	 * V2用 API送信ロジック
	 * @return Result
	 */
	function apiCurlAction($method, $addUrlPass, $data = null)
	{
        
		$header = array(
			"Authorization:Bearer " . API_KEY,
			"Content-Type:application/json",
			"X-Spiral-Api-Version: 1.1",
		);
		if(API_ROLE_ID){
			$header = array_merge($header,array("X-Spiral-App-Role: ".API_ROLE_ID));
		}

		// curl
		$curl = curl_init();
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($curl, CURLOPT_URL, API_URL . $addUrlPass);
		curl_setopt($curl, CURLOPT_HTTPHEADER, $header);

		if ($method == "POST") {
			curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
			curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
		}
		if ($method == "PATCH") {
			curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
			curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
		}
		if ($method == "DELETE") {
			curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
		}
		if ($method == "GET") {
			curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
		}
		$response = curl_exec($curl);
		if (curl_errno($curl)) echo curl_error($curl);
		curl_close($curl);

		return json_decode($response, true);
	}
}

参照フィールドを更新するAPIは、こちらの記事を参考に作成しています。
また、コメント登録の完了画面では、APIが成功時のみ完了メッセージを表示させるよう下記で設定します。

完了画面 - HTML
<div class="sp-form-container"> 
  <div class="sp-form-item sp-form-html" th:inline="none"><p><br></p>
<p><br></p>
<p><br></p>
<p><br></p>
<p><br></p>
<p><br></p>
<th:block th:if="${cp.result.value['insert']} ne 'error'">
    <p style="text-align: center;">コメント登録しました。</p><br>
    <a href="/contactRes"><p style="text-align: center;">お問い合わせ詳細に戻る</p></a>
</th:block>
<th:block th:if="${cp.result.value['insert']} eq 'error'">
    <p style="text-align: center;" th:text="${cp.result.value['errorText']}">text</p>
</th:block>
<p><br></p>
<p><br></p>
<p><br></p>
<p><br></p>
<p><br></p>
<p><br></p></div> 
</div>

手順9:回答チャットページにブロックを埋め込み
手順5,6,7で作成したブロックとヘッダフッタを回答チャットページに埋め込みます。
ページを埋め込む際に、コメント登録完了時にお問い合わせ詳細 / コメント履歴を表示させない処理 と
ステータスが完了になった場合に、コメント登録ブロックを非表示にする処理を入れています。
コメント登録完了時にお問い合わせ詳細 / コメント履歴を表示させない処理に関しては、PHP側で入力画面の判定を入れております。

HTML
<sp:block name="fcb0xxx"></sp:block>
<div class="sp-form-container">
    <th:block th:if="${cp.result.isSuccess}">
        <th:block th:if="${cp.result.value['step']} == 1">
            <sp:block name="fcb0xxx"></sp:block>
            <sp:block name="rlb0xxx"></sp:block>
        </th:block>
        <th:block th:if="${siteClient.record[xx]?.id} ne 4">
            <sp:block name="ifb0xxx"></sp:block>
        </th:block>
    </th:block>
    <div th:if="${!cp.result.isSuccess}">
        <p th:text="${cp.result.errorMessage}">error message</p>
    </div>
</div>
<sp:block name="fcb0xxx"></sp:block>

コメント登録もコピペCSS 「テーブルデザイン」を導入していますが、
お問い合わせ詳細も確認画面と同一のタグ構造で作成しているので、ページにCSSを設定することで共通で使用しています。

また、共通で使用するCSSも一部追加しています。
CSS
a{
    transition: color 0.3s;
    color:#065FD4;
}
a:hover{
    opacity: 0.6;
}
hr {
    margin-top: 70px;
    margin-bottom: 40px;
}
:root {
  /*=============================
	カスタムプロパティ
  ==============================*/
  --base-color: #FFFFFF; /* フォームブロックの背景色 */
  --main-color: #3E4652; /* メインカラー */
  --accent-color: #B8250F; /* アクセントカラー */
  --submit-color: #3E4652; /* 送信ボタンの色 */
  --field-margin: 0px; /* 項目同士の余白の幅 */
  --table-border-color: #D0D0D0; /* 表の枠線の色 */
  --table-label-color: #EBEDF0; /* ラベルセルの背景色 */
  --table-data-color: #FFFFFF; /* 入力欄セルの背景色 */
  --input-color: #FFFFFF; /* 入力欄の背景色 */
  --input-border-color: #3B3C3D; /* 入力欄の枠線の色 */
  --transition: none; /* アニメーション時間 */

  /*------ 合計100%になるよう調整 -------*/
  --label-width: 25%; /* ラベル部分の幅 */
  --data-width: 75%; /* 入力欄部分の幅 */
}

.sp-form-container {
  font-size:16px;
  box-sizing:border-box;
  max-width: 960px;
  color:#505050;
  background-color:var(--base-color);
  border: none;
}

.sp-form-container .sp-form-item:not(:last-child) {
  margin-bottom:0.5em;
  padding:0;
}

.sp-form-field,
.sp-form-group {
  display:-webkit-box;
  display:-ms-flexbox;
  display:flex;
  -webkit-box-orient:vertical;
  -webkit-box-direction:normal;
  -ms-flex-direction:column;
  flex-direction:column;
}

.sp-form-field>.sp-form-label,
.sp-form-group>.sp-form-label {
  display:-webkit-box;
  display:-ms-flexbox;
  display: flex;
  align-items: center;
  font-size:1.15em;
  font-weight:bold;
  padding:.5em 1em;
  word-break:break-all;
  box-sizing:border-box;
  color:#333333;
  background-color: var(--table-label-color);
  border: 1px solid var(--table-border-color);
}

.sp-form-group-item>.sp-form-label {
  word-break:break-all;
  display:inline-block;
  margin-bottom:.2em;
}

.sp-form-field>.sp-form-data,
.sp-form-group>.sp-form-data {
  display:-webkit-box;
  display:-ms-flexbox;
  display:flex;
  justify-content: center;
  -webkit-box-orient:vertical;
  -webkit-box-direction:normal;
  -ms-flex-direction:column;
  flex-direction:column;
  word-break:break-all;
  padding:1.25em 1em;
  border-top: none;
  border-right: 1px solid var(--table-border-color);
  border-bottom: 1px solid var(--table-border-color);
  border-left: 1px solid var(--table-border-color);
  box-sizing:border-box;
}

.sp-form-field>.sp-form-data:last-child,
.sp-form-group>.sp-form-data:last-child {
  border-bottom: 1px solid var(--table-border-color);
}

.sp-form-group-item>.sp-form-data {
  display:-webkit-box;
  display:-ms-flexbox;
  display:flex;
  -webkit-box-orient:vertical;
  -webkit-box-direction:normal;
  -ms-flex-direction:column;
  flex-direction:column;
}

.sp-form-required {
  display:inline-block;
  min-width: max-content;
  height: max-content;
  margin-left: 12px;
  padding: 3px 6px;
  background:var(--accent-color);
  color:#ffffff;
  line-height:1;
  border-radius:.15em;
  font-size:0.7em;
  font-weight: bold;
  vertical-align: middle;
}

.sp-form-group-item:not(:last-child) { margin-bottom:.5em; }

.sp-form-noted {
  font-size:.8em;
  color:#808080;
  word-break:break-all;
  margin-top:.5em;
}

.sp-form-error {
  font-size:.8em;
  color:var(--accent-color);
}

.sp-form-control {
  -moz-appearance:none;
  -webkit-appearance:none;
  -ms-progress-appearance:none;
  appearance:none;
  font-family:inherit;
  font-size:1em;
  line-height:1.5;
  padding:0 .5em;
  background:var(--input-color);
  color:#333333;
  box-sizing:border-box;
  width:100%;
  height:38px;
  border:none;
  border-radius: 3px;
  outline:none;
  box-shadow:0 0 0 0.5px var(--input-border-color);
}
.sp-form-control:focus {
  box-shadow:0 0 0 2px var(--main-color);
}
.sp-form-control[type="number"] {
  height: 36px;
}

.sp-form-control:placeholder-shown { color:#aaa; }

.sp-form-control::placeholder { color:#aaa; }

.sp-form-control::-ms-input-placeholder { color:#aaa; }

.sp-form-control:-ms-input-placeholder { color:#aaa; }

.sp-form-control:-moz-placeholder { color:#aaa; }

textarea.sp-form-control {
  resize:vertical;
  padding-top: 6px;
  height:calc(1.5em * 5);
}

select.sp-form-control,
option.sp-form-control {
  -moz-appearance:none;
  -webkit-appearance:none;
  -ms-progress-appearance:none;
  appearance:none;
  padding-right:1.5em;
}

.sp-form-control>option { background:#FFFFFF;}

select.sp-form-control::-ms-expand { display:none; }

.sp-form-phone .sp-form-control { max-width:20em; }

.sp-form-phone>*:not(:only-child) { margin-right:.5rem; }

.sp-form-dropdown { position:relative; }

.sp-form-dropdown-icon {
  display:block;
  position:absolute;
  top:1.3em;
  right:1em;
  line-height:0;
  pointer-events:none;
}

.sp-form-dropdown-icon:after {
  content:"";
  display:block;
  border-top:0.333em solid #888;
  border-right:.333em solid transparent;
  border-left:.333em solid transparent;
}

.sp-form-selection {
  display:block;
  cursor: pointer;
  margin-bottom:.3em;
  padding:.25em .75em .35em;
  background:var(--input-color);
  border-radius: 3px;
  box-shadow:0 0 0 0.5px var(--input-border-color) ;
}

.sp-form-selection-label {
  display: inline-block;
  position: relative;
  vertical-align:middle;
  padding: 4px 0;
}

.sp-form-datetimes,
.sp-form-date,
.sp-form-time,
.sp-form-timezone {
  display:-webkit-box;
  display:-ms-flexbox;
  display:flex;
  -webkit-box-orient:horizontal;
  -webkit-box-direction:normal;
  -ms-flex-flow:row wrap;
  flex-flow:row wrap;
  -webkit-box-align:center;
  -ms-flex-align:center;
  align-items:center;
}

.sp-form-datetime,
.sp-form-date,
.sp-form-time,
.sp-form-timezone {
  margin-right:.5rem;
  -ms-flex-wrap:nowrap;
  flex-wrap:nowrap;
}

.sp-form-datetime { max-width:20em; }

.sp-form-phone {
  display:-webkit-box;
  display:-ms-flexbox;
  display:flex;
  -webkit-box-orient:horizontal;
  -webkit-box-direction:normal;
  -ms-flex-flow:row wrap;
  flex-flow:row wrap;
  -webkit-box-align:center;
  -ms-flex-align:center;
  align-items:center;
}

.sp-form-date-separator {
  width:1em;
  margin-bottom: .3em;
  text-align:center;
  -ms-flex-negative:1;
  flex-shrink:0;
}

.sp-form-number { max-width:20em; }

.sp-form-email-reenter,
.sp-form-password-reenter {
  font-size:.9em;
  margin-bottom:.2em;
}

.sp-form-html p { margin:0; }

.sp-form-interaction {
  display:-webkit-box;
  display:-ms-flexbox;
  display:flex;
  -ms-flex-wrap:wrap;
  flex-wrap:wrap;
  margin-top:1em;
}

.sp-form-prev-button {
  color:#333333;
  background-color:var(--base-color);
  border:2.0px solid #666666;
}

.sp-form-next-button,
.sp-form-login-button {
  color:#ffffff;
  background-color:var(--submit-color);
  border:none;
}

.sp-form-field .sp-form-embedded { padding-left:.25em; }

.sp-form-selection>input[type="radio"],
.sp-form-selection>input[type="checkbox"] {
  display: none;
  vertical-align:middle;
  margin-left:0;
}

.sp-form-selection>input[type="radio"]+.sp-form-selection-label::before,
.sp-form-selection>input[type="checkbox"]+.sp-form-selection-label::before{
  display: inline-block;
  content:"";
  position: relative;
  box-sizing: border-box;
  top: 4px;
  margin-right: 6px;
  width: 18px;
  height: 18px;
  background: #ffffff;
  border: 9px solid #ffffff;
  box-shadow: 0 0 0 0.5px var(--input-border-color);
  transition: var(--transition);
}

.sp-form-selection>input[type="radio"]+.sp-form-selection-label::before {
  border-radius: 50%;
}

.sp-form-selection>input[type="checkbox"]+.sp-form-selection-label::before {
  border-radius: 2px;
}

.sp-form-selection>input[type="radio"]:checked+.sp-form-selection-label::before{
  background: var(--main-color);
  border:4px solid #ffffff;
}
.sp-form-selection>input[type="checkbox"]+.sp-form-selection-label::after{
  border-right: 3px solid var(--main-color);
  border-bottom: 3px solid var(--main-color);
  content: '';
  display: inline-block;
  width: 5px;
  height: 10px;
  left: 6px;
  margin-top: -6px;
  position: absolute;
  top: 50%;
  transform: rotate(45deg);
  opacity: 0;
  transition: var(--transition);
}

.sp-form-selection>input[type="checkbox"]:checked+.sp-form-selection-label::after{
  left: 5px;
  opacity: 1;
}

.sp-form-date>input,
.sp-form-time>input { max-width:7em; }

.sp-form-date>input[type="date"]{
  min-width: 140px;
  max-width: none;
}
.sp-form-time>input[type="time"] {
  min-width: 92px;
  max-width: none;
}

.sp-form-interaction>button {
  -webkit-box-flex:1;
  -ms-flex-positive:1;
  flex-grow:1;
  cursor:pointer;
  font-size:1em;
  font-weight:bold;
  line-height:1.5;
  margin:.5rem;
  padding:.75rem 0;
  text-align:center;
  -webkit-user-select:none;
  -moz-user-select:none;
  -ms-user-select:none;
  user-select:none;
  white-space:nowrap;
  border-radius: 3px;
}

@media (min-width:768px) {
  .sp-form-container {
    font-size:initial;
    padding:3em;
    margin:2em auto;
    border:1px solid #E1E5EB;
  }

  .sp-form-container .sp-form-item:not(:last-child) {
    margin-bottom:0.5em;
    padding-bottom:0.5em;
  }

  .sp-form-container>.sp-form-item.sp-form-field:not(:last-child) {
    margin-bottom: 0;
    padding-top: 0;
    padding-bottom: 0;
    margin-bottom: calc(var(--field-margin) - 1px);
  }

  .sp-form-field,
  .sp-form-group {
    -webkit-box-orient:horizontal;
    -webkit-box-direction:normal;
    -ms-flex-flow:row nowrap;
    flex-flow:row nowrap;
  }

  .sp-form-field>.sp-form-label,
  .sp-form-group>.sp-form-label {
    justify-content: space-between;
    font-size:1em;
    width:var(--label-width);
    padding: .8em 1.2em;
    background: var(--table-label-color);
    border: 1px solid var(--table-border-color);
  }

  .sp-form-group .sp-form-group-item>.sp-form-label {
    padding-right:.5em;
  }

  .sp-form-field>.sp-form-data,
  .sp-form-group>.sp-form-data {
    font-size:1em;
    width:var(--data-width);
    border-top: 1px solid var(--table-border-color);
    border-left: none;
    padding: .8em 1.2em;
    background: var(--table-data-color);
  }

  .sp-form-group>.sp-form-data {
    -webkit-box-orient:horizontal;
    -webkit-box-direction:normal;
    -ms-flex-flow:row wrap;
    flex-flow:row wrap;
  }

  .sp-form-group.sp-form-group-vertical>.sp-form-data {
    -webkit-box-orient:vertical;
    -webkit-box-direction:normal;
    -ms-flex-direction:column;
    flex-direction:column;
  }

  .sp-form-required {
    margin-left: 0;
    font-size:0.85em;
  }

  .sp-form-group-item { padding:.25em; }

  .sp-form-group-item:not(:last-child) { margin-bottom:0; }

  .sp-form-selection {
    margin:0 .5em 0 0;
    padding-right:1em;
    background:none;
    box-shadow:none;
  }
  .sp-form-selection-vertical>.sp-form-selection {
    margin:0 auto 0 0;
    padding-right: 4em;
  }

  .sp-form-selection-vertical {
    display:-webkit-box;
    display:-ms-flexbox;
    display:flex;
    -webkit-box-orient:vertical;
    -webkit-box-direction:normal;
    -ms-flex-direction:column;
    flex-direction:column;
  }

  .sp-form-selection-horizontal {
    display:-webkit-box;
    display:-ms-flexbox;
    display:flex;
    -webkit-box-orient:horizontal;
    -webkit-box-direction:normal;
    -ms-flex-flow:row wrap;
    flex-flow:row wrap;
  }

  .sp-form-interaction {
    justify-content:center;
    margin-top:2em;
  }

  .sp-form-embedded { font-size:.9em; }

  .sp-form-group-item .sp-form-embedded { padding-right:5em; }

  .sp-form-selection>input[type="radio"]+.sp-form-selection-label::before,
  .sp-form-selection>input[type="checkbox"]+.sp-form-selection-label::before{
    background: var(--input-color);
    border:9px solid var(--input-color);
  }

  .sp-form-selection>input[type="radio"]:hover+.sp-form-selection-label::before,
  .sp-form-selection>input[type="checkbox"]:hover+.sp-form-selection-label::before{
    border:9px solid #dddddd;
  }

  .sp-form-selection>input[type="radio"]:checked+.sp-form-selection-label::before{
    border:4px solid var(--input-color);
  }

  .sp-form-date>input[type="date"]{
    min-width: unset;
  }
  .sp-form-time>input[type="time"] {
    min-width: unset;
  }

  .sp-form-interaction>button {
    -webkit-box-flex:0;
    -ms-flex-positive:0;
    flex-grow:0;
    padding:.75rem 5em;
  }
}

.sp-form-recaptcha {
  display:-webkit-box;
  display:-ms-flexbox;
  display:flex;
  -webkit-box-pack:start;
  -ms-flex-pack:start;
  justify-content:flex-start;
}

.sp-form-recaptcha-right {
  display:-webkit-box;
  display:-ms-flexbox;
  display:flex;
  -webkit-box-pack:end;
  -ms-flex-pack:end;
  justify-content:flex-end;
}

.sp-form-recaptcha-center {
  display:-webkit-box;
  display:-ms-flexbox;
  display:flex;
  -webkit-box-pack:center;
  -ms-flex-pack:center;
  justify-content:center;
}

.sp-form-file-button {
  padding:.8em 2em;
  font-weight: bold;
  cursor:pointer;
  color:#666666;
  background-color:#f6f6f6;
  border:none;
  border: solid 2px #a0a0a0;
  border-radius: 3px;
  transition: var(--transition);
  align-items: center;
  justify-content: center;
}
.sp-form-file-button::before{
  display: inline-block;
  content: url('data:image/svg+xml;utf-8,<svg fill="%23666" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M14.4,7.9l-5.7,5.7c-0.4,0.4-0.8,0.6-1.2,0.8c-0.7,0.3-1.4,0.3-2.1,0.2c-0.7-0.1-1.4-0.5-1.9-1c-0.4-0.4-0.6-0.8-0.8-1.2c-0.3-0.7-0.3-1.4-0.2-2.1c0.1-0.7,0.5-1.4,1-1.9L9.7,2c0.2-0.2,0.5-0.4,0.8-0.5c0.4-0.2,0.9-0.2,1.3-0.1C12.2,1.4,12.6,1.6,13,2c0.2,0.2,0.4,0.5,0.5,0.8c0.2,0.4,0.2,0.9,0.1,1.3c-0.1,0.4-0.3,0.8-0.6,1.2l-6.2,6.2c-0.1,0.1-0.2,0.1-0.3,0.2c-0.1,0.1-0.3,0.1-0.5,0c-0.2,0-0.3-0.1-0.4-0.2c-0.1-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.1-0.3,0-0.5c0-0.2,0.1-0.3,0.2-0.4l5.5-5.5l-0.9-0.9L4.7,9.3C4.5,9.5,4.4,9.7,4.3,10c-0.2,0.4-0.2,0.8-0.1,1.2c0.1,0.4,0.3,0.8,0.6,1.1c0.2,0.2,0.4,0.4,0.7,0.5c0.4,0.2,0.8,0.2,1.2,0.1c0.4-0.1,0.8-0.3,1.1-0.6l6.2-6.2c0.3-0.3,0.6-0.8,0.8-1.2c0.3-0.6,0.3-1.4,0.2-2c-0.1-0.7-0.5-1.3-1-1.8c-0.3-0.3-0.8-0.6-1.2-0.8c-0.6-0.3-1.4-0.3-2-0.2C10,0.2,9.3,0.5,8.8,1L2.5,7.4C2,7.9,1.6,8.5,1.4,9.1C1,10,0.9,11,1.1,11.9c0.2,1,0.6,1.9,1.4,2.6C3,15,3.5,15.4,4.1,15.6C5.1,16,6,16.1,7,15.9c1-0.2,1.9-0.6,2.6-1.4l5.7-5.7L14.4,7.9z"/></svg>');
  margin-left: -0.5em;
  width: 16px;
  height: 100%;
  vertical-align: middle;
}

.sp-form-file-button:focus { outline:0; }

.sp-from-file-default-drag-drop-area {
  color:#333333;
  background-color:var(--input-color);
  border:2.0px dashed #6c757d;
  border-radius:0;
  width:100%;
  height:200px;
  text-align:center;
  border-radius: 3px;
}

.sp-from-file-ondraghover-drag-drop-area {
  color:#333333;
  background-color:#dddddd;
  border:2.0px dashed #6c757d;
  border-radius:0;
  width:100%;
  height:200px;
  text-align:center;
  border-radius: 3px;
}

.sp-from-file-ondragleave-drag-drop-area {
  color:#333333;
  background-color:#dddddd;
  border:2.0px dashed #6c757d;
  border-radius:0;
  width:100%;
  height:200px;
  border-radius: 3px;
}

.sp-from-file-ondrop-drag-drop-area {
  color:#333333;
  background-color:var(--input-color);
  border:2.0px dashed #6c757d;
  border-radius:0;
  width:100%;
  height:200px;
  border-radius: 3px;
}

.sp-form-embedded>.sp-file-info:last-child { padding-bottom:0; }

.sp-file-info {
  padding-top:1em;
  padding-bottom:1em;
  line-height:1.5;
}

.sp-file-info>div { width:100%; }

.main-text {
  font-size:1em;
  font-weight:bold;
}

[class^="sp-step-flow-f"] {
  margin:3em 0 2em;
  }

[class^="sp-step-flow-f"]>.large-setting {
  display:flex;
  justify-content: center;
  background: var(--table-label-color);
  padding: 0;
}

[class^="sp-step-flow-f"]>.small-setting { display:none; }

[class^="sp-step-flow-f"]>.large-setting {
  text-align: center;
  margin: 0 auto;
  counter-reset:step;
}

[class^="sp-step-flow-f"]>.large-setting>.step {
  word-wrap: break-word;
  min-width: 0;
  text-align:center;
}

[class^="sp-step-flow-f"]>.large-setting>.step {
  flex: 1;
  margin-right:36px;
  font-size:16px;
}

[class^="sp-step-flow-f"]>.large-setting>.step:last-child {
  margin-right: 0;
}

[class^="sp-step-flow-f"]>.large-setting>.step>span {
  overflow: hidden;
  text-overflow: ellipsis;
  position:relative;
  display:block;
  padding: 16px 0 18px;
  white-space:nowrap;
  color:#000;
  font-weight:bold;
  opacity: 0.2;
}

[class^="sp-step-flow-f"]>.large-setting>.step.active>span{
  color: #333333;
  opacity: 1;
}

[class^="sp-step-flow-f"]>.large-setting>.step>span::before{
  counter-increment: step;
  content: counter(step)".";
}

[class^="sp-step-flow-f"]>.large-setting>.step.active>span::after{
  position: absolute;
  content: "";
  display: block;
  bottom: 0;
  width: 100%;
  height: 4px;
  background: #505050;
}

@media (max-width: 765px) {
  [class^="sp-step-flow-f"]>.large-setting { display:none; }

  [class^="sp-step-flow-f"]>.small-setting {
    display:flex;
    justify-content: center;
    counter-reset:step;
    margin: 0 auto;
    padding: 0 20px;
    text-align: center;
  }

  [class^="sp-step-flow-f"]>.small-setting>.step>span { display: none; }

  [class^="sp-step-flow-f"]>.small-setting>.step {
    display: flex;
    align-items: center;
    max-width: max-content;
    min-width: 0;
    margin: 0;
    background: none;
    color: #ffffff;
    font-size:14px;
    font-weight: bold;
  }

  [class^="sp-step-flow-f"]>.small-setting>.step::before {
    counter-increment: step 1;
    content: counter(step, decimal);
    min-width:28px;
    height:28px;
    background: var(--main-color);
    border-radius: 1px;
    font-size: 14px;
    line-height: 28px;
  }

  [class^="sp-step-flow-f"]>.small-setting>.step:not(:last-child) { flex-grow: 1; }

  [class^="sp-step-flow-f"]>.small-setting>.step:not(:last-child)::after {
    content: "";
    width: 72px;
    height: 1.5px;
    background: var(--main-color);
    vertical-align: middle;
  }

  [class^="sp-step-flow-f"]>.small-setting>.step.active~.step::before { background:#D3D6DB; }

  [class^="sp-step-flow-f"]>.small-setting>.step.active::after,
  [class^="sp-step-flow-f"]>.small-setting>.step.active~.step::after { background:#F6F6F6; }
}

サイト側の設定は以上となります。
デモのソースコードのため、name値 や リンク先などをマスクしております。
登録やリンクが正しくつながっていない箇所がないか等は、テストの実施をお願いします。
ポイント①
テキストをカレンダー形式で表示させる強化ガジェット導入

テキストをカレンダー形式で表示させる強化ガジェットを導入しています。
期日も設定できるので、回答希望日を翌日以降で設定しています。
ポイント②
コピペCSS 「テーブルデザイン」を使用

今回使用するフォームに関しては、コピペCSS 「テーブルデザイン」を使用しています。
コピペCSSはビジュアル設定でも使用できるため、登録ブロックはビジュアル設定のまま作成できます。
登録ブロック、ログインフォームブロック でコピペCSSを使用しています。

最後に

設定後は動作確認を必ず行い、動作に問題がないか確認をしてください。
また、不具合やほかのやり方が知りたい等あれば、下記の「コンテンツに関しての要望はこちら」からご連絡ください。
サイト設計・構築が完了したので次はユーザ・アプリロール・グループ設定に進みます。

関連記事はこちら
解決しない場合はこちら コンテンツに関しての
要望はこちら