2026-10-15 20:25:00 +08:00
|
|
|
|
{#
|
2026-10-26 00:50:43 +08:00
|
|
|
|
Copyright 2024-2026 ReplyPier
|
2026-10-15 20:25:00 +08:00
|
|
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
|
|
limitations under the License.
|
|
|
|
|
|
|
|
|
|
|
|
A single help-center article: breadcrumb trail (which must stay in step with the
|
|
|
|
|
|
BreadcrumbList JSON-LD), rich Markdown body, "Was this helpful?"
|
|
|
|
|
|
feedback, and related articles. `article.body_html` is the ONLY value rendered
|
|
|
|
|
|
`| safe` — it is pre-sanitized to an HTML allowlist by help_center_content;
|
|
|
|
|
|
every other value stays autoescaped.
|
|
|
|
|
|
#}{% extends "help_center/base.html" %}
|
|
|
|
|
|
|
|
|
|
|
|
{% block body %}
|
|
|
|
|
|
<div class="hc-body">
|
|
|
|
|
|
|
|
|
|
|
|
<!-- ---- SIDEBAR ---- -->
|
|
|
|
|
|
<aside class="hc-aside">
|
|
|
|
|
|
{% if topics %}
|
|
|
|
|
|
<div class="hc-aside-label">BROWSE TOPICS</div>
|
|
|
|
|
|
<div class="hc-topics">
|
|
|
|
|
|
{% for topic in topics %}
|
|
|
|
|
|
<a class="hc-topic{% if topic.active %} active{% endif %}" href="{{ base_path }}/?topic={{ topic.category | urlencode }}" style="--ic: {{ topic.color }};">
|
|
|
|
|
|
<span class="hc-topic-icon">
|
|
|
|
|
|
<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M4 6a2 2 0 0 1 2-2h4l2 2h6a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2z"></path></svg>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span class="hc-topic-name">{{ topic.category }}</span>
|
|
|
|
|
|
<span class="hc-topic-count">{{ topic.count }}</span>
|
|
|
|
|
|
</a>
|
|
|
|
|
|
{% endfor %}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
{% if widget_id %}
|
|
|
|
|
|
<div class="hc-help-card">
|
|
|
|
|
|
<h3>Can't find it?</h3>
|
|
|
|
|
|
<p>Ask our assistant — it answers instantly and can bring in a human.</p>
|
|
|
|
|
|
<button class="hc-cta-btn" type="button" data-chat style="padding: 9px 14px; font-size: 13px; border-radius: 10px; background: var(--brand); color: var(--brand-ink);">
|
|
|
|
|
|
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.5 8.5 0 0 1-12.3 7.6L3 21l1.9-5.7A8.5 8.5 0 1 1 21 11.5z"></path></svg>
|
|
|
|
|
|
Start a chat
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
</aside>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- ---- ARTICLE ---- -->
|
|
|
|
|
|
<main class="hc-main">
|
|
|
|
|
|
<article class="hc-article" style="--ic: {{ article.color }};">
|
|
|
|
|
|
<nav class="hc-crumbs" aria-label="Breadcrumb">
|
|
|
|
|
|
{%- for crumb in breadcrumbs %}
|
|
|
|
|
|
{%- if not loop.first %}<span class="hc-crumb-sep" aria-hidden="true">›</span>{% endif %}
|
|
|
|
|
|
{%- if crumb.href %}
|
|
|
|
|
|
<a href="{{ crumb.href }}">{{ crumb.label }}</a>
|
|
|
|
|
|
{%- else %}
|
|
|
|
|
|
<span class="hc-crumb-current" aria-current="page" title="{{ crumb.label }}">{{ crumb.label }}</span>
|
|
|
|
|
|
{%- endif %}
|
|
|
|
|
|
{%- endfor %}
|
|
|
|
|
|
</nav>
|
|
|
|
|
|
<div class="hc-article-meta">
|
|
|
|
|
|
<span class="hc-article-tag">{{ article.category }}</span>
|
|
|
|
|
|
<span class="hc-article-time">{{ article.read_time }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<h1>{{ article.question }}</h1>
|
|
|
|
|
|
<div class="hc-article-card">
|
|
|
|
|
|
<div class="hc-article-body">{{ article.body_html | safe }}</div>
|
|
|
|
|
|
{% if article.feedback_url %}
|
|
|
|
|
|
<div class="hc-feedback" data-feedback data-feedback-url="{{ article.feedback_url }}">
|
|
|
|
|
|
<span class="hc-feedback-q">Was this helpful?</span>
|
|
|
|
|
|
<button class="hc-vote" type="button" data-vote="yes">
|
|
|
|
|
|
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 11v9H4a1 1 0 0 1-1-1v-7a1 1 0 0 1 1-1z"></path><path d="M7 11l4-8a2 2 0 0 1 3 1.8V9h4.5a2 2 0 0 1 2 2.4l-1.4 7A2 2 0 0 1 17 20H7"></path></svg>
|
|
|
|
|
|
Yes
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button class="hc-vote" type="button" data-vote="no">
|
|
|
|
|
|
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 13V4h3a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1z"></path><path d="M17 13l-4 8a2 2 0 0 1-3-1.8V15H5.5a2 2 0 0 1-2-2.4l1.4-7A2 2 0 0 1 7 4h10"></path></svg>
|
|
|
|
|
|
No
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<span class="hc-thanks" hidden>Thanks for the feedback!</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{% if article.related %}
|
|
|
|
|
|
<div class="hc-related">
|
|
|
|
|
|
<div class="hc-related-label">RELATED ARTICLES</div>
|
|
|
|
|
|
<div class="hc-related-grid">
|
|
|
|
|
|
{% for r in article.related %}
|
|
|
|
|
|
<a class="hc-related-card" href="{{ base_path }}{{ r.href }}" style="--ic: {{ r.color }};">
|
|
|
|
|
|
<span class="hc-related-top">
|
|
|
|
|
|
<span class="hc-related-icon">
|
|
|
|
|
|
<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M4 6a2 2 0 0 1 2-2h4l2 2h6a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2z"></path></svg>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span style="color:#c4c8d2; display:flex;"><svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 17L17 7M17 7H8M17 7v9"></path></svg></span>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span class="hc-related-q">{{ r.question }}</span>
|
|
|
|
|
|
<span class="hc-related-time">
|
|
|
|
|
|
<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3v5h5"></path><path d="M6 3h9l5 5v13H6z"></path></svg>
|
|
|
|
|
|
{{ r.read_time }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</a>
|
|
|
|
|
|
{% endfor %}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
</article>
|
|
|
|
|
|
</main>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{% endblock %}
|
|
|
|
|
|
|
|
|
|
|
|
{% block page_scripts %}
|
|
|
|
|
|
<script>
|
|
|
|
|
|
(function () {
|
|
|
|
|
|
// per-article "Was this helpful?" — records the vote, then acknowledges.
|
|
|
|
|
|
var fb = document.querySelector('[data-feedback]');
|
|
|
|
|
|
if (!fb) return;
|
|
|
|
|
|
var voted = false;
|
|
|
|
|
|
fb.addEventListener('click', function (e) {
|
|
|
|
|
|
var btn = e.target.closest('[data-vote]');
|
|
|
|
|
|
if (!btn || voted) return;
|
|
|
|
|
|
voted = true;
|
|
|
|
|
|
var helpful = btn.getAttribute('data-vote') === 'yes';
|
|
|
|
|
|
fb.querySelectorAll('.hc-vote').forEach(function (v) { v.classList.remove('on-yes', 'on-no'); });
|
|
|
|
|
|
btn.classList.add(helpful ? 'on-yes' : 'on-no');
|
|
|
|
|
|
var thanks = fb.querySelector('.hc-thanks');
|
|
|
|
|
|
if (thanks) thanks.hidden = false;
|
|
|
|
|
|
// Explicit URL, not location.pathname: an article that kept its original
|
|
|
|
|
|
// URL through a migration is served at a path with no /feedback route.
|
|
|
|
|
|
fetch(fb.getAttribute('data-feedback-url'), {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
|
body: JSON.stringify({ helpful: helpful })
|
|
|
|
|
|
}).catch(function () {}); // best-effort; the UI already acknowledged
|
|
|
|
|
|
});
|
|
|
|
|
|
})();
|
|
|
|
|
|
</script>
|
|
|
|
|
|
{% endblock %}
|