1<template>
2 <Transition
3 enter-from-class="opacity-0"
4 enter-active-class="transition-opacity duration-200 motion-reduce:transition-none"
5 >
6 <div v-if="draft" class="space-y-11">
7 <section>
8 <h2 class="text-md-semibold text-ink-gray-8">{{ __("Access") }}</h2>
9 <div class="mt-2 divide-y divide-outline-gray-1">
10 <!-- label-for="": a title click must not flip the switch, so aria-label names it -->
11 <SettingsRow
12 label-for=""
13 v-for="toggle in accessToggles"
14 :key="toggle.fieldname"
15 :title="toggle.label"
16 :description="toggle.description"
17 >
18 <Switch
19 :aria-label="toggle.label"
20 :model-value="Boolean(settings.doc?.[toggle.fieldname])"
21 :disabled="
22 toggle.fieldname === 'allow_anonymous_article_voting' &&
23 !settings.doc?.public_knowledge_base &&
24 !settings.doc?.[toggle.fieldname]
25 "
26 @update:model-value="
27 (value) => setToggle(toggle.fieldname, value)
28 "
29 />
30 </SettingsRow>
31 </div>
32 </section>
33 <section>
34 <h2 class="text-md-semibold text-ink-gray-8">
35 {{ __("Customize home page") }}
36 </h2>
37 <div class="mt-2 divide-y divide-outline-gray-1 [&>*]:py-3.5">
38 <PinnedCategories
39 v-model:pinned="draft.pinned"
40 :categories="categoryOptions"
41 :save="savePinsAndNotify"
42 />
43 <BannerPicker
44 :image="draft.banner_image"
45 :preset="draft.banner_preset"
46 @change="(value) => Object.assign(draft, value)"
47 />
48 </div>
49 </section>
50 <section>
51 <h2 class="text-md-semibold text-ink-gray-8">{{ __("Header") }}</h2>
52 <div class="mt-2 divide-y divide-outline-gray-1 [&>*]:py-3.5">
53 <!-- Stored inverted so unset (existing sites) means shown. -->
54 <SettingsRow
55 label-for=""
56 :title="__('Show Raise a ticket button')"
57 :description="
58 __(
59 'Logged-in customers will see a Raise a ticket button in the knowledge base header.'
60 )
61 "
62 >
63 <Switch
64 :aria-label="__('Show Raise a ticket button')"
65 :model-value="!settings.doc?.hide_raise_ticket_button"
66 @update:model-value="
67 (show) => setToggle('hide_raise_ticket_button', !show)
68 "
69 />
70 </SettingsRow>
71 <HeaderLinks v-model="draft.links" />
72 </div>
73 </section>
74 </div>
75 <SettingsFormSkeleton
76 v-else
77 :sections="[
78 { title: __('Access'), rows: accessToggles.length },
79 { title: __('Customize home page'), panels: [50, 180] },
80 { title: __('Header'), rows: 1, panels: [96] },
81 ]"
82 />
83 </Transition>
84</template>
85
86<script setup lang="ts">
87import { __ } from "../translation";
88import { SettingsRow, Switch } from "frappe-ui";
89import { computed } from "vue";
90import BannerPicker from "./BannerPicker.vue";
91import HeaderLinks from "./HeaderLinks.vue";
92import PinnedCategories from "./PinnedCategories.vue";
93import SettingsFormSkeleton from "./SettingsFormSkeleton.vue";
94import { useKnowledgeBaseDraft } from "./knowledgeBaseDraft";
95
96// Switches and the pin dialog save at once; the rest waits for Save.
97const { draft, settings, setToggle, savePins, categoryOptions } =
98 useKnowledgeBaseDraft();
99
100const emit = defineEmits<{ pinsSaved: [] }>();
101
102async function savePinsAndNotify(pinned: string[]) {
103 const saved = await savePins(pinned);
104 if (saved) emit("pinsSaved");
105 return saved;
106}
107
108// computed, so labels follow a translation load
109const accessToggles = computed(
110 () =>
111 [
112 {
113 fieldname: "public_knowledge_base",
114 label: __("Make knowledge base public"),
115 description: __(
116 "Anyone will be able to read articles without logging in."
117 ),
118 },
119 {
120 fieldname: "allow_anonymous_article_voting",
121 label: __("Allow guests to vote on articles"),
122 description: __(
123 "Visitors who aren't logged in will be able to mark articles as helpful or not."
124 ),
125 },
126 ] as const
127);
128</script>
129