Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(webui): add user guide to nav bar #172

Merged
merged 5 commits into from
Aug 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 1 addition & 18 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,4 @@
- [ ] Statistics: Pull stats based on timeframe
- [ ] Storyteller: Add notes
- [ ] Tests: Increase coverage
- [x] Campaign: Add books to campaigns. Books contain chapters.
- [x] Campaign: Associate characters with campaigns
- [x] Campaign: create channels for each character
- [x] Campaign: Delete book channel when book is deleted
- [x] Campaign: Delete channels when campaign is deleted
- [x] Campaign: If only one campaign, always set it as active
- [x] Campaign: Improve campaign paginator view
- [x] Campaign: Renumber chapters
- [x] Campaign: Rework campaigns to be the backbone of gameplay
- [x] Campaign: View any campaign, not just active one
- [x] Character: assign to other campaign
- [x] Character: rethink inventory
- [x] CharGen: Add edges for hunters
- [x] Refactor: Centralize pagination for long responses
- [x] Statistics: Track per campaign stats
- [x] Storyteller: Associate storyteller characters with campaigns
- [ ] WebUI: Allow production callback url in bot oath settings from Discord developer portal
- [ ] WebUI: Add bind to `0.0.0.0:8000` before release
- [ ] WebUI: Reorder/rename channels in Discord when characters/campaigns are edited
27 changes: 27 additions & 0 deletions src/valentina/webui/WTForms/campaign.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Forms for editing a campaign."""

import uuid

from quart_wtf import QuartForm
from wtforms import StringField, TextAreaField
from wtforms.validators import DataRequired, Length


class CampaignOverviewForm(QuartForm):
"""Form for editing a campaign overview."""

title = "Campaign Overview"
prefix = str(uuid.uuid4())[:8]

name = StringField(
"Campaign Name",
default="",
validators=[DataRequired(), Length(min=3, message="Must be at least 3 characters")],
filters=[str.strip, str.title],
)

description = TextAreaField(
"Description",
validators=[DataRequired(), Length(min=3, message="Must be at least 3 characters")],
description="Markdown is supported",
)
9 changes: 7 additions & 2 deletions src/valentina/webui/blueprints.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from quart import Blueprint

from valentina.webui.views import (
CampaignOverviewSnippet,
CampaignView,
CharacterEdit,
CharacterView,
Expand All @@ -17,10 +18,14 @@
campaign_bp = Blueprint("campaign", __name__)
campaign_bp.add_url_rule(
"/campaign/<string:campaign_id>",
view_func=CampaignView.as_view("view_campaign"),
view_func=CampaignView.as_view("campaign_view"),
methods=["GET", "POST"],
)
campaign_bp.add_url_rule(
"/campaign/<string:campaign_id>/overview",
view_func=CampaignOverviewSnippet.as_view("campaign_overview"),
methods=["GET", "POST"],
)


character_bp = Blueprint("character", __name__)
character_bp.add_url_rule(
Expand Down
11 changes: 6 additions & 5 deletions src/valentina/webui/components/NavBar.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@
<li class="nav-item">
<a class="nav-link btn btn-dark me-2"
aria-current="page"
href="{{ url_for("gameplay.gameplay") }}">Gameplay</a>
href="{{ url_for("gameplay.gameplay") }}"><i class="fa-solid fa-dice"></i>&nbsp;Roll Dice</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle btn btn-dark me-2"
href="#"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false">Characters</a>
aria-expanded="false"><i class="fa-solid fa-users"></i>&nbsp;Characters</a>
<ul class="dropdown-menu">
{% for c_name, c_id in session["USER_CHARACTERS"].items() %}
<li>
Expand All @@ -46,18 +46,19 @@
href="#"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false">Campaigns</a>
aria-expanded="false"><i class="fa-solid fa-book"></i>&nbsp;Campaigns</a>
<ul class="dropdown-menu">
{% for c_name, c_id in session["GUILD_CAMPAIGNS"].items() %}
<li>
<a class="dropdown-item"
href="{{ url_for("campaign.view_campaign", campaign_id=c_id) }}">{{ c_name }}</a>
href="{{ url_for("campaign.campaign_view", campaign_id=c_id) }}">{{ c_name }}</a>
</li>
{% endfor %}
</ul>
</li>
<li class="nav-item">
<a class="nav-link btn btn-dark me-2" aria-disabled="true">Disabled</a>
<a class="nav-link btn btn-dark me-2"
href="{{ url_for("homepage.user_guide") }}"><i class="fa-solid fa-circle-question"></i>&nbsp;User Guide</a>
</li>
</ul>
<!-- NAVBAR RIGHT -->
Expand Down
61 changes: 0 additions & 61 deletions src/valentina/webui/components/global/WTFormContainer.jinja

This file was deleted.

35 changes: 35 additions & 0 deletions src/valentina/webui/components/global/WTFormElements.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{# def
form:QuartForm,
join_label:bool = False,
floating_label:bool = False,
#}
{% if form.form_errors %}
<div class="alert alert-danger" role="alert">
<ul>
{% for error in form.form_errors %}<li>{{ error }}</li>{% endfor %}
</ul>
</div>
{% endif %}
{% for field in form %}
{% set valid_css = " is-invalid" if field.errors else " is-valid" if field.raw_data else "" %}
{% if field.type == "SubmitField" %}
<global.wtform.SubmitField field={{ field }} />
{% elif field.type in ["CSRFTokenField","HiddenField"] %}
{{ field() }}
{% elif field.type == "BooleanField" %}
<global.wtform.BooleanField field={{ field }} join_label={{ join_label }} floating_label={{ floating_label }} />
{% elif field.type == "RadioField" %}
<global.wtform.RadioField field={{ field }} join_label={{ join_label }} floating_label={{ floating_label }} />
{% elif field.type == "ColorField" %}
<global.wtform.ColorField field={{ field }} join_label={{ join_label }} floating_label={{ floating_label }} />
{% elif field.type == "IntegerField" %}
<global.wtform.IntegerField field={{ field }} join_label={{ join_label }} floating_label={{ floating_label }} />
{% elif field.type in ["SelectField", "SelectMultipleField"] %}
<global.wtform.SelectField field={{ field }} join_label={{ join_label }} floating_label={{ floating_label }} />
{% elif field.type in ["IntegerRangeField", "DecimalRangeField"] %}
<global.wtform.RangeField field={{ field }} join_label={{ join_label }} floating_label={{ floating_label }} />
{% else %}
{# All other field types use text inputs #}
<global.wtform.StringField field={{ field }} join_label={{ join_label }} floating_label={{ floating_label }} />
{% endif %}
{% endfor %}
11 changes: 11 additions & 0 deletions src/valentina/webui/routes/home.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Routes for the home page."""

from pathlib import Path

from flask_discord import requires_authorization
from quart import Blueprint, redirect, request, send_from_directory, session, url_for
from quart.wrappers.response import Response as QuartResponse
Expand Down Expand Up @@ -36,3 +38,12 @@ async def select_guild() -> str | Response:
async def static_from_root() -> QuartResponse:
"""Serve a static file from the root directory."""
return await send_from_directory(static_dir, request.path[1:])


@bp.route("/user-guide")
async def user_guide() -> str:
"""Serve the user guide."""
path_to_guide = Path(__file__).parent.parent.parent.parent.parent / "user_guide.md"
guide_content = path_to_guide.read_text()

return catalog.render("user_guide", guide=guide_content)
5 changes: 5 additions & 0 deletions src/valentina/webui/static/valentina.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
.sortable-ghost {
opacity: .5 !important;
background: var(--bs-primary-bg-subtle);
}

form pre {
margin-bottom:0;
font-weight:700;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
{% for book in books | sort(attribute="number") %}
<global.AccordionItem accordion-name="bookAccordion" title={{ book.number ~ '. ' ~ book.name }} index={{ loop.index }}>
{{ book.description_long | from_markdown | safe }}
{% if book.notes %}
<h4>Notes</h4>
<ul>
{% for note in book.notes %}<li>{{ note.text }}</li>{% endfor %}
</ul>
{% endif %}
{% if book.chapters %}
<h4>Chapters</h4>
<ol>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
{% if campaign.npcs %}
<h4>NPCs</h4>
<ul>
{% for npc in campaign.npcs | sort(attribute="full_name") %}
{% for npc in campaign.npcs | sort(attribute="name") %}
<li>
<strong>{{ npc.name | escape }}</strong> {{ npc.description | from_markdown | safe }}
</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
#}
<PageLayout title={{ campaign.name }}>
<global.PageTitle>{{ campaign.name }}</global.PageTitle>
{% include "campaign/tabs.html" %}
{% include "campaign_view/tabs.html" %}
<div id="main-content" class="mt-3 mx-auto px-lg-5">
{# Include the default jinjax template include here for initial page load #}
<campaign.Overview campaign={{ campaign }} />
<campaign_view.Overview campaign={{ campaign }} />
</div>
</PageLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@
</tr>
</table>
</div>
<div class="col">
<h3>Description</h3>
<div class="col" id="campaign-content">
{# This content is overwritten by the CampaignOverviewSnippet view when edited #}
<h3 style="display:inline;">Campaign Description</h3>
<button class="btn btn-sm btn-outline-primary float-end"
hx-get="{{ url_for("campaign.campaign_overview", campaign_id=campaign.id, view="edit") }}"
hx-target="#campaign-content">Edit description</button>
{{ (campaign.description | from_markdown | safe) if campaign.description else "<p>No Description</p>" }}
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{# def
campaign:Campaign,
#}
<h3 style="display:inline;">Campaign Description</h3>
<button class="btn btn-sm btn-outline-primary float-end"
hx-get="{{ url_for("campaign.campaign_overview", campaign_id=campaign.id, view="edit") }}"
hx-target="#campaign-content">Edit description</button>
{{ (campaign.description | from_markdown | safe) if campaign.description else "<p>No Description</p>" }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{# def
form:QuartForm,
join_label:bool = False,
floating_label:bool = False,
post_url:str = "",
campaign:Campaign,
#}
<h3>Edit Campaign</h3>
<form method="post"
id="{{ form._prefix }}form"
hx-post="{{ post_url }}"
novalidate
hx-indicator="#spinner"
hx-target="#campaign-content"
hx-swap="innerHTML"
encoding="application/x-www-form-urlencoded">
<global.WTFormElements form={{ form }} join_label={{ join_label }} floating_label={{ floating_label }} />
<input class="btn btn-success"
id="submit"
name="submit"
type="submit"
value="Submit">
<a href="{{ url_for("campaign.campaign_view", campaign_id=campaign.id) }}"
class="btn btn-outline-secondary ms-2">Cancel</a>
</form>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<global.TabGroup>
<global.TabItem url={{ url_for('campaign.view_campaign', campaign_id=campaign.id, tab='overview') }} text="Overview" number="1" target="#main-content" />
<global.TabItem url={{ url_for('campaign.view_campaign', campaign_id=campaign.id, tab='books') }} text="Books & Chapters" number="2" target="#main-content" />
<global.TabItem url={{ url_for('campaign.view_campaign', campaign_id=campaign.id, tab='characters') }} text="Characters & NPCs" number="3" target="#main-content" />
<global.TabItem url={{ url_for('campaign.view_campaign', campaign_id=campaign.id, tab='statistics') }} text="Statistics" number="4" target="#main-content" />
<global.TabItem url={{ url_for('campaign.campaign_view', campaign_id=campaign.id, tab='overview') }} text="Overview" number="1" target="#main-content" />
<global.TabItem url={{ url_for('campaign.campaign_view', campaign_id=campaign.id, tab='books') }} text="Books & Chapters" number="2" target="#main-content" />
<global.TabItem url={{ url_for('campaign.campaign_view', campaign_id=campaign.id, tab='characters') }} text="Characters & NPCs" number="3" target="#main-content" />
<global.TabItem url={{ url_for('campaign.campaign_view', campaign_id=campaign.id, tab='statistics') }} text="Statistics" number="4" target="#main-content" />
</global.TabGroup>
31 changes: 1 addition & 30 deletions src/valentina/webui/templates/character_create_full/WTForm.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -27,35 +27,6 @@
hx-target="#create-character-container"
{# hx-swap="outerHTML" #}
encoding="{{ encoding }}">
{% if form.form_errors %}
<div class="alert alert-danger" role="alert">
<ul>
{% for error in form.form_errors %}<li>{{ error }}</li>{% endfor %}
</ul>
</div>
{% endif %}
{% for field in form %}
{% set valid_css = " is-invalid" if field.errors else " is-valid" if field.raw_data else "" %}
{% if field.type == "SubmitField" %}
<global.wtform.SubmitField field={{ field }} />
{% elif field.type in ["CSRFTokenField","HiddenField"] %}
{{ field() }}
{% elif field.type == "BooleanField" %}
<global.wtform.BooleanField field={{ field }} join_label={{ join_label }} floating_label={{ floating_label }} />
{% elif field.type == "RadioField" %}
<global.wtform.RadioField field={{ field }} join_label={{ join_label }} floating_label={{ floating_label }} />
{% elif field.type == "ColorField" %}
<global.wtform.ColorField field={{ field }} join_label={{ join_label }} floating_label={{ floating_label }} />
{% elif field.type == "IntegerField" %}
<global.wtform.IntegerField field={{ field }} join_label={{ join_label }} floating_label={{ floating_label }} />
{% elif field.type in ["SelectField", "SelectMultipleField"] %}
<global.wtform.SelectField field={{ field }} join_label={{ join_label }} floating_label={{ floating_label }} />
{% elif field.type in ["IntegerRangeField", "DecimalRangeField"] %}
<global.wtform.RangeField field={{ field }} join_label={{ join_label }} floating_label={{ floating_label }} />
{% else %}
{# All other field types use text inputs #}
<global.wtform.StringField field={{ field }} join_label={{ join_label }} floating_label={{ floating_label }} />
{% endif %}
{% endfor %}
<global.WTFormElements form={{ form }} join_label={{ join_label }} floating_label={{ floating_label }} />
</form>
</div>
Loading