Files
replypier/backend/app/templates/help_center/article.html
T
WangShen007 54b949b392 chore(brand): rename the product to ReplyPier
The upstream project name is gone from code, assets, docs and build
output: brand strings, domains (replypier.site and its app/api/docs/help
subdomains), env prefixes, image and network names, the widget's public
JS API (window.ReplyPier, replypierId/BaseUrl/Config/Token), CSS tokens
(--rp-*), file and directory names, and the WordPress plugin package.

- widget loader is now webclient/replypier.min.js; both bundles rebuilt
  from source (backend/assets/widget.{js,css}, frontend/public/webclient)
- NOTICE, llms.txt, CONTRIBUTING, HELP_CENTER_INFRA and config.dev.js
  rewritten rather than string-swapped; the demo config no longer ships
  the previous vendor's Firebase/Razorpay/Google keys
- new migration replypier_ai_model_001 adds REPLYPIER to the managed
  AIModelType and rewrites pre-rename rows
- local compose project, database, role and volume renamed; LOCAL_DEPLOY
  documents carrying the old local data over
2026-10-26 00:51:03 +08:00

144 lines
7.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{#
Copyright 2024-2026 ReplyPier
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 %}