diff options
Diffstat (limited to 'services/app/templates')
22 files changed, 482 insertions, 0 deletions
diff --git a/services/app/templates/_page.html.twig b/services/app/templates/_page.html.twig new file mode 100644 index 0000000..d2072ea --- /dev/null +++ b/services/app/templates/_page.html.twig @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html> + <head> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>{{ page_title }} | {{ site_name }}</title> + <link rel="stylesheet" href="{{ base_path() }}/assets/index.css"> + <script type="module" src="{{ base_path() }}/assets/index.js"></script> + </head> + <body> + <header class="container"> + <nav class="navbar"> + <a href="{{ url_for('quiz_list') }}" class="navbar-brand">{{ site_name }}</a> + </nav> + </header> + <main class="container"> + <h1>{{ page_title }}</h1> + {% block content %}{% endblock %} + </main> + <footer class="container"> + {{ site_name }} + </footer> + </body> +</html> diff --git a/services/app/templates/admin_answer_edit.html.twig b/services/app/templates/admin_answer_edit.html.twig new file mode 100644 index 0000000..31d007c --- /dev/null +++ b/services/app/templates/admin_answer_edit.html.twig @@ -0,0 +1,44 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <p> + このページは管理画面です + </p> + <h2>{{ quiz.title }}</h2> + <p> + {{ quiz.description }} + </p> + <h2>回答 #{{ answer.answer_number }}</h2> + <p> + {{ answer.author_name }} が {{ answer.submitted_at|date('Y-m-d H:i:s', 'Asia/Tokyo') }} に投稿 + </p> + <h2>コード</h2> + <p> + {{ answer.code_size }} byte + </p> + <pre><code class="hljs language-php">{{ answer.code }}</code></pre> + <h2>実行結果</h2> + <div> + ステータス: {{ answer.execution_status.label() }} + <form action="{{ url_for('admin_answer_rerun_all_testcases_post', { qslug: quiz.slug, anum: answer.answer_number }) }}" method=POST> + <input type="submit" class="btn btn-warning" value="すべてのテストケースを再実行"> + <input type="hidden" name="{{ csrf.name_key }}" value="{{ csrf.name }}"> + <input type="hidden" name="{{ csrf.value_key }}" value="{{ csrf.value }}"> + </form> + </div> + {% for ex in testcase_executions %} + <h3>テストケース {{ loop.index }}</h3> + <div> + ステータス: {{ ex.status.label() }} + </div> + <form action="{{ url_for('admin_answer_rerun_single_testcase_post', { qslug: quiz.slug, anum: answer.answer_number, txid: ex.testcase_execution_id }) }}" method=POST> + <input type="submit" class="btn btn-warning" value="このテストケースを再実行"> + <input type="hidden" name="{{ csrf.name_key }}" value="{{ csrf.name }}"> + <input type="hidden" name="{{ csrf.value_key }}" value="{{ csrf.value }}"> + </form> + <h4>標準出力</h4> + <pre><code class="hljs language-plaintext">{{ ex.stdout }}</code></pre> + <h4>標準エラー出力</h4> + <pre><code class="hljs language-plaintext">{{ ex.stderr }}</code></pre> + {% endfor %} +{% endblock %} diff --git a/services/app/templates/admin_answer_list.html.twig b/services/app/templates/admin_answer_list.html.twig new file mode 100644 index 0000000..68f0089 --- /dev/null +++ b/services/app/templates/admin_answer_list.html.twig @@ -0,0 +1,41 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <p> + このページは管理画面です。<a href="{{ url_for('answer_list', { qslug: quiz.slug }) }}">通常の回答一覧はこちらを参照してください</a> + </p> + <h2>{{ quiz.title }}</h2> + <p> + {{ quiz.description }} + </p> + <h2>回答一覧</h2> + <form action="{{ url_for('admin_answer_rerun_all_answers_post', { qslug: quiz.slug }) }}" method=POST> + <input type="submit" class="btn btn-warning" value="すべての回答に対して全テストケースを再実行"> + <input type="hidden" name="{{ csrf.name_key }}" value="{{ csrf.name }}"> + <input type="hidden" name="{{ csrf.value_key }}" value="{{ csrf.value }}"> + </form> + <table> + <thead> + <tr> + <th>ランク</th> + <th>ID</th> + <th>作者</th> + <th>サイズ</th> + <th>投稿日時</th> + <th>ステータス</th> + </tr> + </thead> + <tbody> + {% for answer in answers %} + <tr> + <td>{{ loop.index }}</td> + <td><a href="{{ url_for('admin_answer_edit', { qslug: quiz.slug, anum: answer.answer_number }) }}">#{{ answer.answer_number }}</a></td> + <td>{{ answer.author_name }}{% if answer.author_is_admin %} (staff){% endif %}</td> + <td>{{ answer.code_size }} byte</td> + <td>{{ answer.submitted_at|date('Y-m-d H:i:s', 'Asia/Tokyo') }}</td> + <td>{{ answer.execution_status.value }}</td> + </tr> + {% endfor %} + </tbody> + </table> +{% endblock %} diff --git a/services/app/templates/admin_overview.html.twig b/services/app/templates/admin_overview.html.twig new file mode 100644 index 0000000..2103616 --- /dev/null +++ b/services/app/templates/admin_overview.html.twig @@ -0,0 +1,13 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <p> + このページは管理画面です + </p> + <div> + <a href="{{ url_for('admin_user_list') }}">ユーザ一覧</a> + </div> + <div> + <a href="{{ url_for('admin_quiz_list') }}">問題一覧</a> + </div> +{% endblock %} diff --git a/services/app/templates/admin_quiz_edit.html.twig b/services/app/templates/admin_quiz_edit.html.twig new file mode 100644 index 0000000..114de02 --- /dev/null +++ b/services/app/templates/admin_quiz_edit.html.twig @@ -0,0 +1,11 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <p> + このページは管理画面です。<a href="{{ url_for('quiz_view', { qslug: quiz.slug }) }}">通常の問題はこちらを参照してください</a> + </p> + {{ include('form/_form.html.twig') }} + <p> + <a href="{{ url_for('admin_testcase_list', { qslug: quiz.slug }) }}">テストケースを追加・削除・編集する</a> + </p> +{% endblock %} diff --git a/services/app/templates/admin_quiz_list.html.twig b/services/app/templates/admin_quiz_list.html.twig new file mode 100644 index 0000000..4db6961 --- /dev/null +++ b/services/app/templates/admin_quiz_list.html.twig @@ -0,0 +1,21 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <p> + このページは管理画面です。<a href="{{ url_for('quiz_list', { qslug: quiz.slug }) }}">通常の問題一覧はこちらを参照してください</a> + </p> + <ul> + {% for quiz in quizzes %} + <li> + <a href="{{ url_for('admin_quiz_edit', { qslug: quiz.slug }) }}">問題 #{{ quiz.quiz_id }}: {{ quiz.title }}</a> + <ul> + <li><a href="{{ url_for('admin_testcase_list', { qslug: quiz.slug }) }}">テストケース一覧</a></li> + <li><a href="{{ url_for('admin_answer_list', { qslug: quiz.slug }) }}">回答一覧</a></li> + </ul> + </li> + {% endfor %} + </ul> + <p> + <a href="{{ url_for('admin_quiz_new') }}">問題を作成する</a> + </p> +{% endblock %} diff --git a/services/app/templates/admin_quiz_new.html.twig b/services/app/templates/admin_quiz_new.html.twig new file mode 100644 index 0000000..9da6a14 --- /dev/null +++ b/services/app/templates/admin_quiz_new.html.twig @@ -0,0 +1,8 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <p> + このページは管理画面です + </p> + {{ include('form/_form.html.twig') }} +{% endblock %} diff --git a/services/app/templates/admin_testcase_edit.html.twig b/services/app/templates/admin_testcase_edit.html.twig new file mode 100644 index 0000000..e86d61b --- /dev/null +++ b/services/app/templates/admin_testcase_edit.html.twig @@ -0,0 +1,22 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <p> + このページは管理画面です + </p> + {{ include('form/_form.html.twig') }} + <p> + 既存のテストケースを編集すると、この問題に対してすでに提出されている回答に対してもテストが再度実行されます。 + その実行が終わるまで回答のステータスは Pending 状態になり、ランキング等にも出現しなくなります。 + </p> + + <form action="{{ url_for('admin_testcase_delete_post', { qslug: quiz.slug, tid: testcase.testcase_id }) }}" method=POST> + <input type="submit" class="btn btn-danger" value="削除"> + <input type="hidden" name="{{ csrf.name_key }}" value="{{ csrf.name }}"> + <input type="hidden" name="{{ csrf.value_key }}" value="{{ csrf.value }}"> + </form> + <p> + 既存のテストケースを削除すると、この問題に対してすでに提出されている回答のステータスは他のテストケースを基にして再計算されます。 + 再計算が終わるまで回答のステータスは Pending 状態になり、ランキング等にも出現しなくなります。 + </p> +{% endblock %} diff --git a/services/app/templates/admin_testcase_list.html.twig b/services/app/templates/admin_testcase_list.html.twig new file mode 100644 index 0000000..a146406 --- /dev/null +++ b/services/app/templates/admin_testcase_list.html.twig @@ -0,0 +1,18 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <p> + このページは管理画面です + </p> + <h2>{{ quiz.title }}</h2> + <p> + {{ quiz.description }} + </p> + <h2>テストケース一覧</h2> + <ul> + {% for testcase in testcases %} + <li><a href="{{ url_for('admin_testcase_edit', { qslug: quiz.slug, tid: testcase.testcase_id }) }}">テストケース #{{ testcase.testcase_id }}</a></li> + {% endfor %} + </ul> + <a href="{{ url_for('admin_testcase_new', { qslug: quiz.slug }) }}">テストケースを追加する</a> +{% endblock %} diff --git a/services/app/templates/admin_testcase_new.html.twig b/services/app/templates/admin_testcase_new.html.twig new file mode 100644 index 0000000..38243a9 --- /dev/null +++ b/services/app/templates/admin_testcase_new.html.twig @@ -0,0 +1,12 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <p> + このページは管理画面です + </p> + {{ include('form/_form.html.twig') }} + <p> + テストケースを追加すると、この問題に対してすでに提出されている回答に対してもテストが実行されます。 + その実行が終わるまで回答のステータスは Pending 状態になり、ランキング等にも出現しなくなります。 + </p> +{% endblock %} diff --git a/services/app/templates/admin_user_edit.html.twig b/services/app/templates/admin_user_edit.html.twig new file mode 100644 index 0000000..9da6a14 --- /dev/null +++ b/services/app/templates/admin_user_edit.html.twig @@ -0,0 +1,8 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <p> + このページは管理画面です + </p> + {{ include('form/_form.html.twig') }} +{% endblock %} diff --git a/services/app/templates/admin_user_list.html.twig b/services/app/templates/admin_user_list.html.twig new file mode 100644 index 0000000..5d477a9 --- /dev/null +++ b/services/app/templates/admin_user_list.html.twig @@ -0,0 +1,14 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <p> + このページは管理画面です + </p> + <ul> + {% for user in users %} + <li> + <a href="{{ url_for('admin_user_edit', { username: user.username }) }}">{{ user.username }}</a>{% if user.is_admin %} (管理者){% endif %} + </li> + {% endfor %} + </ul> +{% endblock %} diff --git a/services/app/templates/answer_list.html.twig b/services/app/templates/answer_list.html.twig new file mode 100644 index 0000000..410ab20 --- /dev/null +++ b/services/app/templates/answer_list.html.twig @@ -0,0 +1,45 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <h2>{{ quiz.title }}</h2> + <p> + {{ quiz.description }} + </p> + <h2>回答一覧</h2> + {% if answers|length == 0 %} + <p> + まだ回答がありません + </p> + {% else %} + <table> + <thead> + <tr> + <th>ランク</th> + <th>ID</th> + <th>作者</th> + <th>サイズ</th> + <th>投稿日時</th> + <th>ステータス</th> + </tr> + </thead> + <tbody> + {% for answer in answers %} + <tr> + <td> + {% if is_ranking_hidden %} + ? + {% else %} + {{ loop.index }} + {% endif %} + </td> + <td><a href="{{ url_for('answer_view', { qslug: quiz.slug, anum: answer.answer_number }) }}">#{{ answer.answer_number }}</a></td> + <td>{{ answer.author_name }}{% if answer.author_is_admin %} (staff){% endif %}</td> + <td>{{ answer.code_size }} byte</td> + <td>{{ answer.submitted_at|date('Y-m-d H:i:s', 'Asia/Tokyo') }}</td> + <td>{{ answer.execution_status.label() }}</td> + </tr> + {% endfor %} + </tbody> + </table> + {% endif %} +{% endblock %} diff --git a/services/app/templates/answer_new.html.twig b/services/app/templates/answer_new.html.twig new file mode 100644 index 0000000..9dbf96e --- /dev/null +++ b/services/app/templates/answer_new.html.twig @@ -0,0 +1,12 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <h2>{{ quiz.title }}</h2> + <p> + {{ quiz.description }} + </p> + <h3>実装例</h3> + <pre><code class="hljs language-php">{{ quiz.example_code }}</code></pre> + <h3>回答</h3> + {{ include('form/_form.html.twig') }} +{% endblock %} diff --git a/services/app/templates/answer_view.html.twig b/services/app/templates/answer_view.html.twig new file mode 100644 index 0000000..a76e161 --- /dev/null +++ b/services/app/templates/answer_view.html.twig @@ -0,0 +1,38 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <h2>{{ quiz.title }}</h2> + <p> + {{ quiz.description }} + </p> + <h2>回答 #{{ answer.answer_number }}</h2> + <p> + {{ answer.author_name }} が {{ answer.submitted_at|date('Y-m-d H:i:s', 'Asia/Tokyo') }} に投稿 + </p> + <h2>コード</h2> + <p> + {{ answer.code_size }} byte + </p> + <pre><code class="hljs language-php">{{ answer.code }}</code></pre> + <h2>実行結果</h2> + <div> + ステータス: <span class="js-aggregated-execution-status" data-answer-id="{{ answer.answer_id }}">{{ answer.execution_status.label() }}</span> + {% if answer.execution_status.showLoadingIndicator() %} + <div class="js-aggregated-execution-status-loading-indicator spinner-border text-primary spinner-border-sm" role="status"><span class="visually-hidden">Loading...</span></div> + {% endif %} + </div> + {% for ex in testcase_executions %} + <h3>テストケース {{ loop.index }}</h3> + <div> + ステータス: <span class="js-testcase-execution-status" data-testcase-execution-id="{{ ex.testcase_execution_id }}">{{ ex.status.label() }}</span> + {% if ex.status.showLoadingIndicator() %} + <div class="js-testcase-execution-status-loading-indicator spinner-border text-primary spinner-border-sm" role="status" data-testcase-execution-id="{{ ex.testcase_execution_id }}"><span class="visually-hidden">Loading...</span></div> + {% endif %} + </div> + <h4>標準出力</h4> + <pre><code class="js-testcase-execution-stdout hljs language-plaintext" data-testcase-execution-id="{{ ex.testcase_execution_id }}">{{ ex.stdout }}</code></pre> + <h4>標準エラー出力</h4> + <pre><code class="js-testcase-execution-stderr hljs language-plaintext" data-testcase-execution-id="{{ ex.testcase_execution_id }}">{{ ex.stderr }}</code></pre> + {% endfor %} + <script type="module" src="{{ base_path() }}/assets/loading.js"></script> +{% endblock %} diff --git a/services/app/templates/form/_form.html.twig b/services/app/templates/form/_form.html.twig new file mode 100644 index 0000000..b6a9d1d --- /dev/null +++ b/services/app/templates/form/_form.html.twig @@ -0,0 +1,24 @@ +<form method=POST{% if form.action %} action="{{ form.action }}"{% endif %} novalidate> + {% if form.errors.general %} + <div class="alert alert-danger"> + {{ form.errors.general }} + </div> + {% endif %} + + {% for item in form.items %} + {% if item.type == 'checkbox' %} + {{ include('form/_form_item_checkbox.html.twig') }} + {% elseif item.type == 'textarea' %} + {{ include('form/_form_item_textarea.html.twig') }} + {% else %} + {{ include('form/_form_item.html.twig') }} + {% endif %} + {% endfor %} + + <div class="mb-3"> + <input type="submit" class="btn btn-primary" value="{{ form.submit_label }}"> + </div> + + <input type="hidden" name="{{ csrf.name_key }}" value="{{ csrf.name }}"> + <input type="hidden" name="{{ csrf.value_key }}" value="{{ csrf.value }}"> +</form> diff --git a/services/app/templates/form/_form_item.html.twig b/services/app/templates/form/_form_item.html.twig new file mode 100644 index 0000000..6e6a36b --- /dev/null +++ b/services/app/templates/form/_form_item.html.twig @@ -0,0 +1,20 @@ +{% set value = form.state[item.name] %} +{% set error = form.errors[item.name] %} +{% set classes = 'form-control' %} +{% if error %} + {% set classes = classes ~ ' is-invalid' %} +{% endif %} +{% set required = item.isRequired ? ' required' : '' %} +{% set disabled = item.isDisabled ? ' disabled' : '' %} +{% set extra = item.extra ? item.extra : '' %} +<div class="mb-3"> + {% if item.label %} + <label for="form-{{ item.name }}" class="form-label">{{ item.label }}</label> + {% endif %} + + <input type="{{ item.type }}" class="{{ classes }}" id="form-{{ item.name }}" name="{{ item.name }}"{% if value %} value="{{ value }}"{% endif %}{{ required }}{{ disabled }} {{ extra }}> + + {% if error %} + <div class="invalid-feedback">{{ error }}</div> + {% endif %} +</div> diff --git a/services/app/templates/form/_form_item_checkbox.html.twig b/services/app/templates/form/_form_item_checkbox.html.twig new file mode 100644 index 0000000..e51cc0c --- /dev/null +++ b/services/app/templates/form/_form_item_checkbox.html.twig @@ -0,0 +1,21 @@ +{% set value = form.state[item.name] %} +{% set error = form.errors[item.name] %} +{% set classes = 'form-check-input' %} +{% if error %} + {% set classes = classes ~ ' is-invalid' %} +{% endif %} +{% set required = item.isRequired ? ' required' : '' %} +{% set disabled = item.isDisabled ? ' disabled' : '' %} +{% set extra = item.extra ? item.extra : '' %} +{% set extra = extra ~ (value == 'on' ? ' checked' : '') %} +<div class="mb-3 form-check"> + <input type="checkbox" class="{{ classes }}" id="form-{{ item.name }}" name="{{ item.name }}"{% if value %} value="{{ value }}"{% endif %}{{ required }}{{ disabled }} {{ extra }}> + + {% if item.label %} + <label for="form-{{ item.name }}" class="form-check-label">{{ item.label }}</label> + {% endif %} + + {% if error %} + <div class="invalid-feedback">{{ error }}</div> + {% endif %} +</div> diff --git a/services/app/templates/form/_form_item_textarea.html.twig b/services/app/templates/form/_form_item_textarea.html.twig new file mode 100644 index 0000000..bf5cc8c --- /dev/null +++ b/services/app/templates/form/_form_item_textarea.html.twig @@ -0,0 +1,20 @@ +{% set value = form.state[item.name] %} +{% set error = form.errors[item.name] %} +{% set classes = 'form-control' %} +{% if error %} + {% set classes = classes ~ ' is-invalid' %} +{% endif %} +{% set required = item.isRequired ? ' required' : '' %} +{% set disabled = item.isDisabled ? ' disabled' : '' %} +{% set extra = item.extra ? item.extra : '' %} +<div class="mb-3"> + {% if item.label %} + <label for="form-{{ item.name }}" class="form-label">{{ item.label }}</label> + {% endif %} + + <textarea class="{{ classes }}" id="form-{{ item.name }}" name="{{ item.name }}"{{ required }}{{ disabled }} {{ extra }}>{{ value }}</textarea> + + {% if error %} + <div class="invalid-feedback">{{ error }}</div> + {% endif %} +</div> diff --git a/services/app/templates/login.html.twig b/services/app/templates/login.html.twig new file mode 100644 index 0000000..e21b32f --- /dev/null +++ b/services/app/templates/login.html.twig @@ -0,0 +1,5 @@ +{% extends '_page.html.twig' %} + +{% block content %} + {{ include('form/_form.html.twig') }} +{% endblock %} diff --git a/services/app/templates/quiz_list.html.twig b/services/app/templates/quiz_list.html.twig new file mode 100644 index 0000000..2aa4f98 --- /dev/null +++ b/services/app/templates/quiz_list.html.twig @@ -0,0 +1,9 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <ul> + {% for quiz in quizzes %} + <li><a href="{{ url_for('quiz_view', { qslug: quiz.slug }) }}">問題 #{{ quiz.quiz_id }}: {{ quiz.title }}</a></li> + {% endfor %} + </ul> +{% endblock %} diff --git a/services/app/templates/quiz_view.html.twig b/services/app/templates/quiz_view.html.twig new file mode 100644 index 0000000..87c83ce --- /dev/null +++ b/services/app/templates/quiz_view.html.twig @@ -0,0 +1,53 @@ +{% extends '_page.html.twig' %} + +{% block content %} + <h2>{{ quiz.title }}</h2> + <p> + {{ quiz.description }} + </p> + <h3>実装例</h3> + <pre><code class="hljs language-php">{{ quiz.example_code }}</code></pre> + {% if is_open %} + <p> + <a href="{{ url_for('answer_new', { qslug: quiz.slug }) }}">回答する</a> + </p> + {% endif %} + <h2>ランキング</h2> + {% if is_ranking_hidden %} + 回答が締め切られるまで、ランキングは表示されません + {% elseif ranking|length == 0 %} + <p> + まだ正解した回答がありません + </p> + {% else %} + <table> + <thead> + <tr> + <th>ランク</th> + <th>ID</th> + <th>作者</th> + <th>サイズ</th> + <th>投稿日時</th> + </tr> + </thead> + <tbody> + {% for answer in ranking %} + <tr> + <td>{{ loop.index }}</td> + <td><a href="{{ url_for('answer_view', { qslug: quiz.slug, anum: answer.answer_number }) }}">#{{ answer.answer_number }}</a></td> + <td>{{ answer.author_name }}{% if answer.author_is_admin %} (staff){% endif %}</td> + <td>{{ answer.code_size }} byte</td> + <td>{{ answer.submitted_at|date('Y-m-d H:i:s', 'Asia/Tokyo') }}</td> + </tr> + {% endfor %} + </tbody> + </table> + <div> + <canvas id="chart" data-quiz-id="{{ quiz.quiz_id }}"></canvas> + <script type="module" src="{{ base_path() }}/assets/chart.js"></script> + </div> + {% endif %} + <p> + <a href="{{ url_for('answer_list', { qslug: quiz.slug }) }}">すべての回答を見る</a> + </p> +{% endblock %} |
