1<template>
2 <!-- Wait for settings: early off switches would look saved. -->
3 <Transition
4 enter-from-class="opacity-0"
5 enter-active-class="transition-opacity duration-200 motion-reduce:transition-none"
6 >
7 <div v-if="ready" class="space-y-11">
8 <section v-for="section in sections" :key="section.title">
9 <h2 class="text-md-semibold text-ink-gray-8">{{ section.title }}</h2>
10 <div class="mt-2 divide-y divide-outline-gray-1">
11 <!-- label-for="": a title click must not flip the switch, so aria-label names it -->
12 <SettingsRow
13 label-for=""
14 v-for="toggle in section.toggles"
15 :key="toggle.fieldname"
16 :title="toggle.label"
17 :description="toggle.description"
18 >
19 <Switch
20 :aria-label="toggle.label"
21 :model-value="
22 Boolean(toggle.settings.doc?.[toggle.fieldname]) !==
23 Boolean(toggle.inverted)
24 "
25 :disabled="toggle.disabled"
26 @update:model-value="
27 (value) => save(toggle, value !== Boolean(toggle.inverted))
28 "
29 />
30 </SettingsRow>
31 </div>
32 </section>
33 </div>
34 <SettingsFormSkeleton
35 v-else
36 :sections="
37 sections.map((section) => ({
38 title: section.title,
39 rows: section.toggles.length,
40 }))
41 "
42 />
43 </Transition>
44</template>
45
46<script setup lang="ts">
47import { __ } from "../translation";
48import {
49 createDocumentResource,
50 createResource,
51 getCachedDocumentResource,
52 SettingsRow,
53 Switch,
54 toast,
55} from "frappe-ui";
56import { computed } from "vue";
57import SettingsFormSkeleton from "./SettingsFormSkeleton.vue";
58
59// The portal mounts all panels at once; re-creating a cached doc refetches.
60const odSettings =
61 getCachedDocumentResource("OD Settings", "OD Settings") ||
62 createDocumentResource({ doctype: "OD Settings", name: "OD Settings" });
63
64const websiteSettings = createDocumentResource({
65 doctype: "Website Settings",
66 name: "Website Settings",
67});
68
69// Only admins can write Website Settings; others would hit PermissionError.
70const canEditSignup = createResource({
71 url: "frappe.client.has_permission",
72 params: {
73 doctype: "Website Settings",
74 docname: "Website Settings",
75 perm_type: "write",
76 },
77 auto: true,
78});
79
80const ready = computed(
81 () =>
82 Boolean(odSettings.doc) &&
83 canEditSignup.fetched &&
84 (!canEditSignup.data?.has_permission || Boolean(websiteSettings.doc))
85);
86
87// computed, so labels follow a translation load
88const sections = computed(() => [
89 {
90 title: __("Access"),
91 toggles: [
92 {
93 settings: odSettings,
94 fieldname: "prefer_knowledge_base",
95 label: __("Use knowledge base as customer home"),
96 description: __(
97 "Customers will land on the knowledge base when they log in, instead of their tickets."
98 ),
99 },
100 {
101 settings: odSettings,
102 fieldname: "knowledge_base_home_page_for_guests",
103 label: __("Set knowledge base as guest home"),
104 description: __(
105 "Guests land on the knowledge base landing page instead of the login page."
106 ),
107 // Guests only reach a public knowledge base. Left on, it can still be turned off.
108 disabled:
109 !odSettings.doc?.public_knowledge_base &&
110 !odSettings.doc?.knowledge_base_home_page_for_guests,
111 },
112 // Shown as "Allow": the field stores the opposite. Counted while
113 // unknown, so the skeleton has a row for it.
114 ...(!canEditSignup.fetched || canEditSignup.data?.has_permission
115 ? [
116 {
117 settings: websiteSettings,
118 fieldname: "disable_signup",
119 inverted: true,
120 label: __("Allow signup"),
121 description: __(
122 "Anyone can create a portal account. When off, system managers register new users."
123 ),
124 },
125 ]
126 : []),
127 ],
128 },
129 {
130 title: __("Customer managers"),
131 toggles: [
132 {
133 settings: odSettings,
134 fieldname: "allow_customer_managers_to_manage_organization",
135 label: __("Allow organization management"),
136 description: __(
137 "Change member roles, remove members and change the organization's logo."
138 ),
139 },
140 {
141 settings: odSettings,
142 fieldname: "allow_customer_managers_to_invite",
143 label: __("Allow member invites"),
144 description: __(
145 "Invite people to their organization. Invited people get a portal login."
146 ),
147 },
148 ],
149 },
150]);
151
152function save(toggle, value: boolean) {
153 toggle.settings.setValue.submit(
154 { [toggle.fieldname]: value },
155 { onSuccess: () => toast.success(__("Settings updated")) }
156 );
157}
158</script>
159