mirror of
https://github.com/abhinavxd/libredesk.git
synced 2026-09-10 22:25:39 +00:00
move help center branding columns into theme and ship Instrument Sans
This commit is contained in:
+10
-16
@@ -584,7 +584,8 @@ func handleShowHelpCenterHome(r *fastglue.Request) error {
|
||||
root = helpCenterBaseURL(app, helpCenter)
|
||||
locales = helpCenterLocales(helpCenter)
|
||||
pathFor = func(l string) string { return helpCenterHomePath(helpCenter, l) }
|
||||
metaDescription = firstNonEmpty(tree.HelpCenter.MetaDescription, tree.HelpCenter.HeaderText)
|
||||
theme = helpCenterTheme(tree.HelpCenter)
|
||||
metaDescription = firstNonEmpty(tree.HelpCenter.MetaDescription, theme.Header.Heading)
|
||||
)
|
||||
data := helpCenterTemplateData(app, tree.HelpCenter, locale)
|
||||
return renderHelpCenterPage(r, hcPageName(helpCenter, "help-center"), map[string]interface{}{
|
||||
@@ -594,7 +595,7 @@ func handleShowHelpCenterHome(r *fastglue.Request) error {
|
||||
"MetaDescription": metaDescription,
|
||||
"CanonicalPath": pathFor(locale),
|
||||
"LandingHero": true,
|
||||
"OGImage": absoluteURL(root, publicAssetPaths(app, tree.HelpCenter.LogoURL)),
|
||||
"OGImage": absoluteURL(root, publicAssetPaths(app, theme.LogoURL)),
|
||||
"Alternates": helpCenterAlternates(helpCenter, locales, pathFor),
|
||||
"XDefaultPath": defaultLocalePath(helpCenter, locales, pathFor),
|
||||
"LocaleLinks": helpCenterLocaleLinks(helpCenter, locales, pathFor),
|
||||
@@ -647,7 +648,7 @@ func handleShowHelpCenterCollection(r *fastglue.Request) error {
|
||||
"Title": fmt.Sprintf("%s - %s", collection.Name, helpCenter.Name),
|
||||
"MetaDescription": collection.Description,
|
||||
"CanonicalPath": pathFor(locale),
|
||||
"OGImage": absoluteURL(root, publicAssetPaths(app, helpCenter.LogoURL)),
|
||||
"OGImage": absoluteURL(root, publicAssetPaths(app, helpCenterTheme(helpCenter).LogoURL)),
|
||||
"Alternates": helpCenterAlternates(helpCenter, translated, pathFor),
|
||||
"XDefaultPath": defaultLocalePath(helpCenter, translated, pathFor),
|
||||
"LocaleLinks": helpCenterLocaleLinks(helpCenter, translated, pathFor),
|
||||
@@ -715,7 +716,7 @@ func handleShowHelpCenterArticle(r *fastglue.Request) error {
|
||||
pathFor = func(l string) string { return articlePath(helpCenter, l, article.Slug) }
|
||||
metaDescription = firstNonEmpty(article.MetaDescription, article.Excerpt)
|
||||
metaTitle = firstNonEmpty(article.MetaTitle, fmt.Sprintf("%s - %s", article.Title, helpCenter.Name))
|
||||
ogImage = absoluteURL(root, publicAssetPaths(app, firstNonEmpty(article.MetaImageURL, helpCenter.LogoURL)))
|
||||
ogImage = absoluteURL(root, publicAssetPaths(app, firstNonEmpty(article.MetaImageURL, helpCenterTheme(helpCenter).LogoURL)))
|
||||
)
|
||||
data := helpCenterTemplateData(app, helpCenter, locale)
|
||||
return renderHelpCenterPage(r, hcPageName(helpCenter, "help-article"), map[string]interface{}{
|
||||
@@ -1330,7 +1331,7 @@ func homeJSONLD(root string, hc hcmodels.HelpCenter, locale string) template.JS
|
||||
"query-input": "required name=search_term_string",
|
||||
},
|
||||
}
|
||||
if d := firstNonEmpty(hc.MetaDescription, hc.HeaderText); d != "" {
|
||||
if d := firstNonEmpty(hc.MetaDescription, helpCenterTheme(hc).Header.Heading); d != "" {
|
||||
site["description"] = d
|
||||
}
|
||||
return jsonLD([]any{site})
|
||||
@@ -1545,12 +1546,6 @@ func sidebarTree(app *App, hc hcmodels.HelpCenter, locale string) []hcmodels.Tre
|
||||
|
||||
// helpCenterTemplateData shapes a help center row for the public templates.
|
||||
func helpCenterTemplateData(app *App, hc hcmodels.HelpCenter, locale string) map[string]interface{} {
|
||||
navLinks := []hcmodels.NavLink{}
|
||||
if len(hc.NavLinks) > 0 {
|
||||
if err := json.Unmarshal(hc.NavLinks, &navLinks); err != nil {
|
||||
navLinks = nil
|
||||
}
|
||||
}
|
||||
theme := helpCenterTheme(hc)
|
||||
theme.Favicon = publicAssetPaths(app, theme.Favicon)
|
||||
theme.Header.BackgroundImage = publicAssetPaths(app, theme.Header.BackgroundImage)
|
||||
@@ -1565,15 +1560,15 @@ func helpCenterTemplateData(app *App, hc hcmodels.HelpCenter, locale string) map
|
||||
"BaseURL": helpCenterBaseURL(app, hc),
|
||||
"BasePath": helpCenterHomePath(hc, locale),
|
||||
"PageTitle": hc.PageTitle,
|
||||
"HeaderText": hc.HeaderText,
|
||||
"LogoURL": publicAssetPaths(app, hc.LogoURL),
|
||||
"Color": hc.Color,
|
||||
"HeaderText": theme.Header.Heading,
|
||||
"LogoURL": publicAssetPaths(app, theme.LogoURL),
|
||||
"Color": theme.Color,
|
||||
"DefaultLocale": hc.DefaultLocale,
|
||||
"CurrentLocale": locale,
|
||||
"OGLocale": strings.ReplaceAll(locale, "-", "_"),
|
||||
"Dir": localeDir(locale),
|
||||
"AvailableLocales": helpCenterLocales(hc),
|
||||
"NavLinks": navLinks,
|
||||
"NavLinks": theme.NavLinks,
|
||||
"Theme": theme,
|
||||
"ThemeCSS": buildThemeCSSVars(theme),
|
||||
"AnnouncementKey": announcementKey(hc.Slug, theme.Announcement),
|
||||
@@ -1681,7 +1676,6 @@ func validateHelpCenter(r *fastglue.Request, req *helpcenter.HelpCenterRequest)
|
||||
if req.PageTitle == "" {
|
||||
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.empty", "name", "`page_title`"), nil, envelope.InputError)
|
||||
}
|
||||
req.LogoURL = publicAssetPaths(app, req.LogoURL)
|
||||
req.Theme = json.RawMessage(publicAssetPaths(app, string(req.Theme)))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<LinkListField name="nav_links" :label="t('helpCenter.navLinks')" />
|
||||
<LinkListField name="theme.nav_links" :label="t('helpCenter.navLinks')" />
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label>{{ t('helpCenter.supportedLanguages') }}</Label>
|
||||
@@ -151,7 +151,7 @@
|
||||
:open="openSection === 'brand'"
|
||||
@toggle="toggleSection('brand')"
|
||||
>
|
||||
<FormField v-slot="{ componentField }" name="logo_url">
|
||||
<FormField v-slot="{ componentField }" name="theme.logo_url">
|
||||
<FormItem>
|
||||
<FormLabel>{{ t('globals.terms.logoUrl') }}</FormLabel>
|
||||
<FormControl>
|
||||
@@ -161,7 +161,7 @@
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ componentField }" name="color">
|
||||
<FormField v-slot="{ componentField }" name="theme.color">
|
||||
<FormItem>
|
||||
<FormLabel>{{ t('globals.terms.primaryColor') }}</FormLabel>
|
||||
<FormControl>
|
||||
@@ -191,7 +191,7 @@
|
||||
:open="openSection === 'header'"
|
||||
@toggle="toggleSection('header')"
|
||||
>
|
||||
<FormField v-slot="{ componentField }" name="header_text">
|
||||
<FormField v-slot="{ componentField }" name="theme.header.heading">
|
||||
<FormItem>
|
||||
<FormLabel>{{ t('helpCenter.headerText') }}</FormLabel>
|
||||
<FormControl>
|
||||
@@ -666,12 +666,12 @@ import { useI18n } from 'vue-i18n'
|
||||
// Field path prefix -> [tab, section] to reveal when that field errors on submit.
|
||||
const FIELD_LOCATION = [
|
||||
['theme.header', 'appearance', 'header'],
|
||||
['header_text', 'appearance', 'header'],
|
||||
['theme.tagline', 'appearance', 'header'],
|
||||
['theme.announcement', 'appearance', 'announcement'],
|
||||
['logo_url', 'appearance', 'brand'],
|
||||
['color', 'appearance', 'brand'],
|
||||
['theme.logo_url', 'appearance', 'brand'],
|
||||
['theme.color', 'appearance', 'brand'],
|
||||
['theme.favicon', 'appearance', 'brand'],
|
||||
['theme.nav_links', 'general', ''],
|
||||
['theme.layout', 'appearance', 'landing'],
|
||||
['theme.cards', 'appearance', 'landing'],
|
||||
['theme.article', 'appearance', 'article'],
|
||||
@@ -720,20 +720,20 @@ const toFormValues = (hc) => ({
|
||||
template: hc?.template === 'docs' ? 'docs' : 'classic',
|
||||
custom_domain: hc?.custom_domain || '',
|
||||
page_title: hc?.page_title || '',
|
||||
header_text: hc?.header_text || '',
|
||||
meta_description: hc?.meta_description || '',
|
||||
logo_url: hc?.logo_url || '',
|
||||
color: hc?.color || '#1f93ff',
|
||||
nav_links: Array.isArray(hc?.nav_links) ? hc.nav_links : [],
|
||||
custom_css: hc?.custom_css || '',
|
||||
custom_js: hc?.custom_js || '',
|
||||
default_locale: hc?.default_locale || 'en',
|
||||
allowed_locales:
|
||||
Array.isArray(hc?.allowed_locales) && hc.allowed_locales.length ? hc.allowed_locales : ['en'],
|
||||
theme: {
|
||||
color: hc?.theme?.color || '#1f93ff',
|
||||
logo_url: hc?.theme?.logo_url || '',
|
||||
nav_links: Array.isArray(hc?.theme?.nav_links) ? hc.theme.nav_links : [],
|
||||
favicon: hc?.theme?.favicon || '',
|
||||
tagline: hc?.theme?.tagline || '',
|
||||
header: {
|
||||
heading: hc?.theme?.header?.heading || '',
|
||||
background_type: hc?.theme?.header?.background_type || 'default',
|
||||
background_color: hc?.theme?.header?.background_color || '#1f93ff',
|
||||
gradient_from: hc?.theme?.header?.gradient_from || '#1f93ff',
|
||||
@@ -829,7 +829,6 @@ watch(localeOptions, (locales) => {
|
||||
const toPayload = (values) => {
|
||||
const payload = JSON.parse(JSON.stringify(values))
|
||||
const allowed = cleanLocales(payload.allowed_locales)
|
||||
payload.nav_links = payload.nav_links || []
|
||||
payload.allowed_locales = allowed.length ? allowed : ['en']
|
||||
if (payload.theme?.layout) {
|
||||
payload.theme.layout.columns = Number(payload.theme.layout.columns) || 2
|
||||
|
||||
@@ -49,11 +49,7 @@ const createBaseSchema = (t) => {
|
||||
.regex(/^[a-z0-9_-]+$/, t('helpCenter.invalidSlug')),
|
||||
page_title: z.string().min(1, t('globals.messages.required')),
|
||||
template: z.enum(['docs', 'classic']).default('classic'),
|
||||
header_text: z.string().optional(),
|
||||
meta_description: z.string().optional(),
|
||||
logo_url: optionalURL,
|
||||
color: z.string().optional(),
|
||||
nav_links: linkArray,
|
||||
custom_domain: z
|
||||
.string()
|
||||
.refine(
|
||||
@@ -79,10 +75,14 @@ const createBaseSchema = (t) => {
|
||||
.default(['en']),
|
||||
theme: z
|
||||
.object({
|
||||
color: z.string().optional(),
|
||||
logo_url: optionalURL,
|
||||
nav_links: linkArray,
|
||||
favicon: optionalURL,
|
||||
tagline: z.string().optional(),
|
||||
header: z
|
||||
.object({
|
||||
heading: z.string().optional(),
|
||||
background_type: z.string().optional(),
|
||||
background_color: optionalHexColor,
|
||||
gradient_from: optionalHexColor,
|
||||
|
||||
@@ -96,11 +96,7 @@ type HelpCenterRequest struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
PageTitle string `json:"page_title"`
|
||||
HeaderText string `json:"header_text"`
|
||||
MetaDescription string `json:"meta_description"`
|
||||
LogoURL string `json:"logo_url"`
|
||||
Color string `json:"color"`
|
||||
NavLinks json.RawMessage `json:"nav_links"`
|
||||
CustomCSS string `json:"custom_css"`
|
||||
CustomJS string `json:"custom_js"`
|
||||
DefaultLocale string `json:"default_locale"`
|
||||
@@ -281,13 +277,10 @@ func (m *Manager) CreateHelpCenter(req HelpCenterRequest) (models.HelpCenter, er
|
||||
if err := m.validateLocales(req.DefaultLocale, req.AllowedLocales); err != nil {
|
||||
return hc, err
|
||||
}
|
||||
if err := m.validateColor(req.Color); err != nil {
|
||||
return hc, err
|
||||
}
|
||||
if err := m.validateCustomDomain(req.CustomDomain, 0); err != nil {
|
||||
return hc, err
|
||||
}
|
||||
if err := m.q.InsertHelpCenter.Get(&hc, req.Name, req.Slug, req.PageTitle, req.HeaderText, req.MetaDescription, req.LogoURL, req.Color, req.NavLinks, req.CustomCSS, req.CustomJS, req.DefaultLocale, req.AllowedLocales, req.Theme, req.CustomDomain, req.Template); err != nil {
|
||||
if err := m.q.InsertHelpCenter.Get(&hc, req.Name, req.Slug, req.PageTitle, req.MetaDescription, req.CustomCSS, req.CustomJS, req.DefaultLocale, req.AllowedLocales, req.Theme, req.CustomDomain, req.Template); err != nil {
|
||||
if dbutil.IsUniqueViolationError(err) {
|
||||
return hc, envelope.NewError(envelope.ConflictError, m.i18n.T("globals.messages.errorAlreadyExists"), nil)
|
||||
}
|
||||
@@ -306,11 +299,7 @@ func (m *Manager) DraftHelpCenter(id int, req HelpCenterRequest) (models.HelpCen
|
||||
req = normalizeHelpCenterRequest(req)
|
||||
hc.Name = req.Name
|
||||
hc.PageTitle = req.PageTitle
|
||||
hc.HeaderText = req.HeaderText
|
||||
hc.MetaDescription = req.MetaDescription
|
||||
hc.LogoURL = req.LogoURL
|
||||
hc.Color = req.Color
|
||||
hc.NavLinks = req.NavLinks
|
||||
hc.CustomCSS = req.CustomCSS
|
||||
hc.CustomJS = req.CustomJS
|
||||
hc.DefaultLocale = req.DefaultLocale
|
||||
@@ -318,9 +307,6 @@ func (m *Manager) DraftHelpCenter(id int, req HelpCenterRequest) (models.HelpCen
|
||||
hc.Theme = req.Theme
|
||||
hc.CustomDomain = req.CustomDomain
|
||||
hc.Template = req.Template
|
||||
if err := m.validateColor(hc.Color); err != nil {
|
||||
return hc, err
|
||||
}
|
||||
return hc, nil
|
||||
}
|
||||
|
||||
@@ -337,13 +323,10 @@ func (m *Manager) UpdateHelpCenter(id int, req HelpCenterRequest) (models.HelpCe
|
||||
if err := m.validateLocalesRetained(id, req.AllowedLocales); err != nil {
|
||||
return hc, err
|
||||
}
|
||||
if err := m.validateColor(req.Color); err != nil {
|
||||
return hc, err
|
||||
}
|
||||
if err := m.validateCustomDomain(req.CustomDomain, id); err != nil {
|
||||
return hc, err
|
||||
}
|
||||
if err := m.q.UpdateHelpCenter.Get(&hc, id, req.Name, req.Slug, req.PageTitle, req.HeaderText, req.MetaDescription, req.LogoURL, req.Color, req.NavLinks, req.CustomCSS, req.CustomJS, req.DefaultLocale, req.AllowedLocales, req.Theme, req.CustomDomain, req.Template); err != nil {
|
||||
if err := m.q.UpdateHelpCenter.Get(&hc, id, req.Name, req.Slug, req.PageTitle, req.MetaDescription, req.CustomCSS, req.CustomJS, req.DefaultLocale, req.AllowedLocales, req.Theme, req.CustomDomain, req.Template); err != nil {
|
||||
if dbutil.IsUniqueViolationError(err) {
|
||||
return hc, envelope.NewError(envelope.ConflictError, m.i18n.T("globals.messages.errorAlreadyExists"), nil)
|
||||
}
|
||||
@@ -1403,13 +1386,6 @@ func (m *Manager) validateCustomDomain(domain string, excludeID int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) validateColor(color string) error {
|
||||
if !hexColorRe.MatchString(color) {
|
||||
return envelope.NewError(envelope.InputError, m.i18n.T("helpCenter.invalidColor"), nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateHelpCenterServesLocale rejects a locale the help center doesn't list.
|
||||
func (m *Manager) validateHelpCenterServesLocale(helpCenterID int, locale string) error {
|
||||
hc, err := m.GetHelpCenterByID(helpCenterID)
|
||||
@@ -1445,15 +1421,10 @@ func normalizeHelpCenterRequest(req HelpCenterRequest) HelpCenterRequest {
|
||||
if req.DefaultLocale == "" {
|
||||
req.DefaultLocale = defaultLocale
|
||||
}
|
||||
if req.Color == "" {
|
||||
req.Color = defaultAccentColor
|
||||
}
|
||||
if !slices.Contains(helpCenterTemplates, req.Template) {
|
||||
req.Template = models.TemplateClassic
|
||||
}
|
||||
req.CustomDomain = strings.TrimRight(strings.TrimSpace(req.CustomDomain), "/")
|
||||
req.LogoURL = sanitizeAssetURL(req.LogoURL)
|
||||
req.NavLinks = normalizeNavLinks(req.NavLinks)
|
||||
|
||||
locales := normalizeLocales(parseLocales(req.AllowedLocales), req.DefaultLocale)
|
||||
if b, err := json.Marshal(locales); err == nil {
|
||||
@@ -1463,23 +1434,6 @@ func normalizeHelpCenterRequest(req HelpCenterRequest) HelpCenterRequest {
|
||||
return req
|
||||
}
|
||||
|
||||
// normalizeNavLinks drops header links that aren't absolute or root-relative. Invalid JSON collapses to '[]'.
|
||||
func normalizeNavLinks(raw json.RawMessage) json.RawMessage {
|
||||
empty := json.RawMessage("[]")
|
||||
if len(raw) == 0 {
|
||||
return empty
|
||||
}
|
||||
var links []models.NavLink
|
||||
if err := json.Unmarshal(raw, &links); err != nil {
|
||||
return empty
|
||||
}
|
||||
b, err := json.Marshal(sanitizeNavLinks(links))
|
||||
if err != nil {
|
||||
return empty
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// normalizeTheme drops any theme color that isn't a valid hex code before it can reach
|
||||
// the injected CSS. Invalid JSON collapses to '{}'.
|
||||
func normalizeTheme(raw json.RawMessage) json.RawMessage {
|
||||
@@ -1490,6 +1444,13 @@ func normalizeTheme(raw json.RawMessage) json.RawMessage {
|
||||
if err := json.Unmarshal(raw, &t); err != nil {
|
||||
return json.RawMessage("{}")
|
||||
}
|
||||
t.Color = sanitizeHexColor(t.Color)
|
||||
if t.Color == "" {
|
||||
t.Color = defaultAccentColor
|
||||
}
|
||||
t.LogoURL = sanitizeAssetURL(t.LogoURL)
|
||||
t.NavLinks = sanitizeNavLinks(t.NavLinks)
|
||||
t.Header.Heading = strings.TrimSpace(t.Header.Heading)
|
||||
t.Header.BackgroundColor = sanitizeHexColor(t.Header.BackgroundColor)
|
||||
t.Header.GradientFrom = sanitizeHexColor(t.Header.GradientFrom)
|
||||
t.Header.GradientTo = sanitizeHexColor(t.Header.GradientTo)
|
||||
|
||||
@@ -20,11 +20,7 @@ type HelpCenter struct {
|
||||
Name string `db:"name" json:"name"`
|
||||
Slug string `db:"slug" json:"slug"`
|
||||
PageTitle string `db:"page_title" json:"page_title"`
|
||||
HeaderText string `db:"header_text" json:"header_text"`
|
||||
MetaDescription string `db:"meta_description" json:"meta_description"`
|
||||
LogoURL string `db:"logo_url" json:"logo_url"`
|
||||
Color string `db:"color" json:"color"`
|
||||
NavLinks json.RawMessage `db:"nav_links" json:"nav_links"`
|
||||
CustomCSS string `db:"custom_css" json:"custom_css"`
|
||||
CustomJS string `db:"custom_js" json:"custom_js"`
|
||||
DefaultLocale string `db:"default_locale" json:"default_locale"`
|
||||
@@ -37,6 +33,9 @@ type HelpCenter struct {
|
||||
|
||||
// Theme holds the customizable branding for a help center's public pages.
|
||||
type Theme struct {
|
||||
Color string `json:"color"`
|
||||
LogoURL string `json:"logo_url"`
|
||||
NavLinks []NavLink `json:"nav_links"`
|
||||
Favicon string `json:"favicon"`
|
||||
Tagline string `json:"tagline"`
|
||||
Header HeaderTheme `json:"header"`
|
||||
@@ -57,6 +56,7 @@ type AnnouncementTheme struct {
|
||||
}
|
||||
|
||||
type HeaderTheme struct {
|
||||
Heading string `json:"heading"`
|
||||
BackgroundType string `json:"background_type"` // "solid" | "gradient" | "image"
|
||||
BackgroundColor string `json:"background_color"`
|
||||
GradientFrom string `json:"gradient_from"`
|
||||
@@ -194,6 +194,7 @@ type Insights struct {
|
||||
// DefaultTheme enables the show-flags for elements that must keep rendering when a stored theme predates the flag.
|
||||
func DefaultTheme() Theme {
|
||||
return Theme{
|
||||
Color: "#1f93ff",
|
||||
Layout: LayoutTheme{ShowPopularArticles: true},
|
||||
Cards: CardTheme{ShowIconTile: true},
|
||||
Article: ArticleTheme{ShowAuthor: true},
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
-- name: get-all-help-centers
|
||||
SELECT id, created_at, updated_at, name, slug, page_title, header_text, meta_description, logo_url, color, nav_links, custom_css, custom_js, default_locale, allowed_locales, is_active, theme, custom_domain, template
|
||||
SELECT id, created_at, updated_at, name, slug, page_title, meta_description, custom_css, custom_js, default_locale, allowed_locales, is_active, theme, custom_domain, template
|
||||
FROM help_centers
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
-- name: get-active-help-centers
|
||||
SELECT id, created_at, updated_at, name, slug, page_title, header_text, meta_description, logo_url, color, nav_links, custom_css, custom_js, default_locale, allowed_locales, is_active, theme, custom_domain, template
|
||||
SELECT id, created_at, updated_at, name, slug, page_title, meta_description, custom_css, custom_js, default_locale, allowed_locales, is_active, theme, custom_domain, template
|
||||
FROM help_centers
|
||||
WHERE is_active = true
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
-- name: get-help-center-by-id
|
||||
SELECT id, created_at, updated_at, name, slug, page_title, header_text, meta_description, logo_url, color, nav_links, custom_css, custom_js, default_locale, allowed_locales, is_active, theme, custom_domain, template
|
||||
SELECT id, created_at, updated_at, name, slug, page_title, meta_description, custom_css, custom_js, default_locale, allowed_locales, is_active, theme, custom_domain, template
|
||||
FROM help_centers
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: get-help-center-by-slug
|
||||
SELECT id, created_at, updated_at, name, slug, page_title, header_text, meta_description, logo_url, color, nav_links, custom_css, custom_js, default_locale, allowed_locales, is_active, theme, custom_domain, template
|
||||
SELECT id, created_at, updated_at, name, slug, page_title, meta_description, custom_css, custom_js, default_locale, allowed_locales, is_active, theme, custom_domain, template
|
||||
FROM help_centers
|
||||
WHERE slug = $1 AND is_active = true;
|
||||
|
||||
-- name: insert-help-center
|
||||
INSERT INTO help_centers (name, slug, page_title, header_text, meta_description, logo_url, color, nav_links, custom_css, custom_js, default_locale, allowed_locales, theme, custom_domain, template)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
|
||||
INSERT INTO help_centers (name, slug, page_title, meta_description, custom_css, custom_js, default_locale, allowed_locales, theme, custom_domain, template)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
RETURNING *;
|
||||
|
||||
-- name: update-help-center
|
||||
UPDATE help_centers
|
||||
SET name = $2, slug = $3, page_title = $4, header_text = $5, meta_description = $6, logo_url = $7, color = $8, nav_links = $9, custom_css = $10, custom_js = $11, default_locale = $12, allowed_locales = $13, theme = $14, custom_domain = $15, template = $16, updated_at = NOW()
|
||||
SET name = $2, slug = $3, page_title = $4, meta_description = $5, custom_css = $6, custom_js = $7, default_locale = $8, allowed_locales = $9, theme = $10, custom_domain = $11, template = $12, updated_at = NOW()
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
|
||||
@@ -48,11 +48,7 @@ func V2_8_0(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error {
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
page_title TEXT NOT NULL DEFAULT '',
|
||||
header_text TEXT NOT NULL DEFAULT '',
|
||||
meta_description TEXT NOT NULL DEFAULT '',
|
||||
logo_url TEXT NOT NULL DEFAULT '',
|
||||
color TEXT NOT NULL DEFAULT '#1f93ff',
|
||||
nav_links JSONB NOT NULL DEFAULT '[]',
|
||||
custom_css TEXT NOT NULL DEFAULT '',
|
||||
custom_js TEXT NOT NULL DEFAULT '',
|
||||
default_locale TEXT NOT NULL DEFAULT 'en',
|
||||
|
||||
@@ -670,11 +670,7 @@ CREATE TABLE help_centers (
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
page_title TEXT NOT NULL DEFAULT '',
|
||||
header_text TEXT NOT NULL DEFAULT '',
|
||||
meta_description TEXT NOT NULL DEFAULT '',
|
||||
logo_url TEXT NOT NULL DEFAULT '',
|
||||
color TEXT NOT NULL DEFAULT '#1f93ff',
|
||||
nav_links JSONB NOT NULL DEFAULT '[]',
|
||||
custom_css TEXT NOT NULL DEFAULT '',
|
||||
custom_js TEXT NOT NULL DEFAULT '',
|
||||
default_locale TEXT NOT NULL DEFAULT 'en',
|
||||
|
||||
@@ -26,7 +26,7 @@ body {
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-family: "Instrument Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
color: var(--hc-ink);
|
||||
background: var(--hc-page);
|
||||
@@ -96,7 +96,7 @@ a:hover { text-decoration: underline; }
|
||||
.hc-hero h1 {
|
||||
margin: 0 0 1.75rem;
|
||||
font-size: clamp(1.7rem, 3.5vw, 2.1rem);
|
||||
font-weight: 800;
|
||||
font-weight: 700;
|
||||
letter-spacing: -.025em;
|
||||
line-height: 1.18;
|
||||
color: var(--hc-header-text, var(--hc-ink));
|
||||
@@ -176,7 +176,7 @@ main:has(> article.hc-article), main:has(> .hc-collection-head), main:has(> .hc-
|
||||
.hc-breadcrumb li + li::before { content: "/"; color: var(--hc-faint); margin-right: .4rem; }
|
||||
.hc-breadcrumb .hc-current { color: var(--hc-ink); }
|
||||
.hc-page-title {
|
||||
font-size: clamp(1.7rem, 3.5vw, 2.1rem); font-weight: 800;
|
||||
font-size: clamp(1.7rem, 3.5vw, 2.1rem); font-weight: 700;
|
||||
letter-spacing: -.025em; line-height: 1.18; margin: 0 0 1.5rem;
|
||||
}
|
||||
/* Popular articles */
|
||||
@@ -215,7 +215,7 @@ main:has(> article.hc-article), main:has(> .hc-collection-head), main:has(> .hc-
|
||||
.hc-collection-head { margin-bottom: 1.25rem; display: flex; align-items: flex-start; gap: .9rem; }
|
||||
.hc-collection-head .hc-collection-icon { margin: 6px 0 0; }
|
||||
.hc-collection-headings { min-width: 0; }
|
||||
.hc-collection-head h1 { font-size: clamp(1.7rem, 3.5vw, 2.1rem); font-weight: 800; letter-spacing: -.025em; line-height: 1.18; margin: 0 0 .5rem; }
|
||||
.hc-collection-head h1 { font-size: clamp(1.7rem, 3.5vw, 2.1rem); font-weight: 700; letter-spacing: -.025em; line-height: 1.18; margin: 0 0 .5rem; }
|
||||
.hc-collection-head .hc-desc { color: var(--hc-muted); margin: 0; font-size: 1rem; }
|
||||
.hc-article-panel {
|
||||
padding: .35rem 1.25rem;
|
||||
@@ -263,7 +263,7 @@ main:has(> article.hc-article), main:has(> .hc-collection-head), main:has(> .hc-
|
||||
|
||||
/* Article */
|
||||
article.hc-article { max-width: var(--hc-article-width); }
|
||||
article.hc-article > h1 { font-size: clamp(1.7rem, 3.5vw, 2.1rem); font-weight: 800; letter-spacing: -.025em; line-height: 1.18; margin: .25rem 0 .6rem; }
|
||||
article.hc-article > h1 { font-size: clamp(1.7rem, 3.5vw, 2.1rem); font-weight: 700; letter-spacing: -.025em; line-height: 1.18; margin: .25rem 0 .6rem; }
|
||||
.hc-article-excerpt { color: var(--hc-muted); font-size: 1.05rem; line-height: 1.6; margin: 0 0 1rem; }
|
||||
article.hc-article .hc-meta { color: var(--hc-faint); font-size: .86rem; margin: 0 0 2rem; padding-bottom: 1.25rem; border-bottom: 1px solid var(--hc-border); }
|
||||
|
||||
@@ -445,7 +445,7 @@ article.hc-article .hc-meta-byline { display: flex; align-items: center; gap: .6
|
||||
/* Not found */
|
||||
.hc-notfound { max-width: 34rem; margin: 1rem auto; text-align: center; }
|
||||
.hc-notfound .hc-code { font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .12em; color: var(--hc-accent-ink); margin: 0 0 .75rem; }
|
||||
.hc-notfound h1 { font-size: clamp(1.7rem, 3.5vw, 2.1rem); font-weight: 800; letter-spacing: -.025em; line-height: 1.18; margin: 0 0 .75rem; }
|
||||
.hc-notfound h1 { font-size: clamp(1.7rem, 3.5vw, 2.1rem); font-weight: 700; letter-spacing: -.025em; line-height: 1.18; margin: 0 0 .75rem; }
|
||||
.hc-notfound p { color: var(--hc-muted); margin: 0 0 1.75rem; }
|
||||
.hc-notfound .hc-back {
|
||||
display: inline-flex; align-items: center; gap: .5rem;
|
||||
|
||||
@@ -27,7 +27,7 @@ body.hcd {
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-family: "Instrument Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
color: var(--hc-ink);
|
||||
background: var(--hc-bg);
|
||||
@@ -294,7 +294,7 @@ main#hc-main > * { max-width: var(--hcd-content-w); }
|
||||
margin: 0 0 .5rem;
|
||||
max-width: 34rem;
|
||||
font-size: clamp(1.9rem, 3.4vw, 2.6rem);
|
||||
font-weight: 800; letter-spacing: -.032em; line-height: 1.12;
|
||||
font-weight: 700; letter-spacing: -.032em; line-height: 1.12;
|
||||
}
|
||||
.hcd-hero-tagline { margin: 0 0 1.5rem; font-size: 1.05rem; color: var(--hc-muted); }
|
||||
.hcd-hero h1:last-child { margin-bottom: 1.5rem; }
|
||||
@@ -333,7 +333,7 @@ main#hc-main > .hcd-collections-section { max-width: calc(var(--hcd-content-w) +
|
||||
.hcd-collection-head .hc-collection-icon { margin: .45rem 0 0; }
|
||||
.hcd-collection-headings { min-width: 0; }
|
||||
.hcd-collection-head h1, .hcd-page-title, .hcd-article h1 {
|
||||
font-size: clamp(1.55rem, 3vw, 2rem); font-weight: 800; letter-spacing: -.025em; line-height: 1.18; margin: 0 0 .4rem;
|
||||
font-size: clamp(1.55rem, 3vw, 2rem); font-weight: 700; letter-spacing: -.025em; line-height: 1.18; margin: 0 0 .4rem;
|
||||
}
|
||||
.hcd-lead { color: var(--hc-muted); font-size: 1.02rem; line-height: 1.6; margin: 0; }
|
||||
|
||||
@@ -486,7 +486,7 @@ main#hc-main > .hcd-collections-section { max-width: calc(var(--hcd-content-w) +
|
||||
}
|
||||
.hcd-notfound { margin: 6rem auto 3rem; max-width: 34rem; text-align: center; }
|
||||
.hcd-code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .8rem; font-weight: 600; letter-spacing: .18em; color: var(--hc-faint); margin: 0 0 .75rem; }
|
||||
.hcd-notfound h1 { font-size: clamp(1.7rem, 3.2vw, 2.4rem); font-weight: 800; letter-spacing: -.03em; margin: 0 0 .6rem; }
|
||||
.hcd-notfound h1 { font-size: clamp(1.7rem, 3.2vw, 2.4rem); font-weight: 700; letter-spacing: -.03em; margin: 0 0 .6rem; }
|
||||
.hcd-notfound p { color: var(--hc-muted); margin: 0 0 1.5rem; }
|
||||
.hcd-notfound-search {
|
||||
display: flex; align-items: center; gap: .7rem;
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
{{ if .Data.OGImage }}<meta name="twitter:image" content="{{ .Data.OGImage }}" />{{ end }}
|
||||
<meta name="theme-color" content="{{ .Data.HelpCenter.Color }}" />
|
||||
{{ if .Data.HelpCenter.Theme.Favicon }}<link rel="icon" href="{{ .Data.HelpCenter.Theme.Favicon }}" />{{ else if ne FaviconURL "" }}<link rel="icon" href="{{ FaviconURL }}" />{{ end }}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400..700&display=swap" />
|
||||
<link rel="stylesheet" href="/static/public/static/help-center-cards.css?v={{ AssetVer }}" />
|
||||
<link rel="stylesheet" href="/static/public/static/help-center-{{ .Data.HelpCenter.Template }}.css?v={{ AssetVer }}" />
|
||||
<link rel="stylesheet" href="/static/public/static/article-content.css?v={{ AssetVer }}" />
|
||||
|
||||
Reference in New Issue
Block a user