1<template>
2 <SettingsLayoutBase>
3 <template #title>
4 <div class="flex items-center gap-2">
5 <h1 class="text-md-semibold text-ink-gray-8">
6 {{ __("Field Dependencies") }}
7 </h1>
8 </div>
9 </template>
10 <template #description>
11 <p class="text-p-sm max-w-md text-ink-gray-6">
12 {{
13 __(
14 "Create field dependencies to dynamically update options based on user selections. Learn more about field dependencies"
15 )
16 }}
17 <a
18 href="https://github.com/ikhsanddy/opendesk"
19 target="_blank"
20 class="underline"
21 >{{ __("here.") }}</a
22 >
23 </p>
24 </template>
25 <template #header-actions>
26 <Button
27 :label="__('New')"
28 theme="gray"
29 variant="solid"
30 @click="$emit('update:step', 'fd')"
31 icon-left="lucide-plus"
32 class="rtl:flex-row-reverse"
33 />
34 </template>
35 <template #content>
36 <div class="grow">
37 <!-- Loading State -->
38 <div
39 v-if="fieldDependenciesList.loading"
40 class="flex items-center justify-center py-4"
41 >
42 <LoadingIndicator class="size-5" />
43 </div>
44
45 <!-- Empty State -->
46 <EmptyState
47 v-if="
48 !fieldDependenciesList.loading &&
49 !fieldDependenciesList.data?.length
50 "
51 variant="badge"
52 :icon="FieldDependencyIcon"
53 title="No field dependency found"
54 description="Add one to get started."
55 />
56
57 <div
58 class="w-full -ms-2"
59 v-if="
60 !fieldDependenciesList.loading &&
61 fieldDependenciesList.data?.length > 0
62 "
63 >
64 <div>
65 <div
66 class="grid grid-cols-11 items-center gap-4 text-sm text-ink-gray-5"
67 >
68 <div class="col-span-7 ms-2">{{ __("Name") }}</div>
69 <div class="col-span-2">{{ __("Created by") }}</div>
70 <div class="col-span-2">{{ __("Enabled") }}</div>
71 </div>
72 <hr class="mt-2 mx-2" />
73 <div
74 v-for="(row, index) in fieldDependenciesList.data"
75 :key="row.name"
76 >
77 <div
78 class="grid grid-cols-11 items-center gap-4 cursor-pointer hover:bg-surface-sidebar rounded-4 h-12.5"
79 >
80 <div
81 @click.stop="$emit('update:step', 'fd', row.name)"
82 class="w-full py-3 ps-2 col-span-7 text-base text-ink-gray-7"
83 >
84 <span>{{ getFieldDependencyLabel(row.name) }}</span>
85 </div>
86 <div class="col-span-2 flex items-center gap-1">
87 <Avatar size="sm" :image="row.owner" :label="row.owner" />
88 <span class="text-base text-ink-gray-7 truncate">{{
89 row.owner
90 }}</span>
91 </div>
92 <div
93 class="flex justify-between items-center w-full pe-2 col-span-2"
94 >
95 <div>
96 <Switch
97 :model-value="Boolean(row.enabled)"
98 @update:modelValue="(e) => handleSwitchToggle(row, e)"
99 @click.stop
100 />
101 </div>
102 <div>
103 <Dropdown
104 align="end"
105 :options="getOptions(row.name)"
106 @update:open="
107 (open) => open && (isConfirmingDelete = false)
108 "
109 >
110 <Button
111 variant="ghost"
112 @click.stop="
113 () => {
114 isConfirmingDelete = false;
115 }
116 "
117 >
118 <template #icon>
119 <LucideMoreHorizontal class="h-4 w-4" />
120 </template>
121 </Button>
122 </Dropdown>
123 </div>
124 </div>
125 </div>
126 <hr
127 v-if="index !== fieldDependenciesList.data.length - 1"
128 class="mx-2"
129 />
130 </div>
131 </div>
132 </div>
133 </div>
134 </template>
135 </SettingsLayoutBase>
136</template>
137
138<script setup lang="ts">
139import {
140 Avatar,
141 Button,
142 Dropdown,
143 LoadingIndicator,
144 Switch,
145 toast,
146} from "frappe-ui";
147import EmptyState from "@/components/EmptyState.vue";
148import { getFieldDependencyLabel, ConfirmDelete } from "@/utils";
149import { onMounted, ref } from "vue";
150import { fieldDependenciesList } from "./fieldDependency";
151import FieldDependencyIcon from "@/components/icons/FieldDependencyIcon.vue";
152import { __ } from "@/translation";
153import SettingsLayoutBase from "@/components/layouts/SettingsLayoutBase.vue";
154
155onMounted(() => {
156 fieldDependenciesList.reload();
157});
158
159const isConfirmingDelete = ref(false);
160
161function getOptions(rowName: string) {
162 return ConfirmDelete({
163 isConfirmingDelete,
164 onConfirmDelete: () => {
165 fieldDependenciesList.delete.submit(rowName, {
166 onSuccess: () => {
167 toast.success(__("Field dependency deleted successfully."));
168 fieldDependenciesList.reload();
169 },
170 });
171 },
172 });
173}
174
175function handleSwitchToggle(row: any, value: boolean) {
176 // Optimistically reflect the new state so the controlled Switch stays in
177 // sync; without this the bound value never changes and toggling keeps
178 // re-sending the same value (the disable never takes effect).
179 row.enabled = value;
180 fieldDependenciesList.setValue.submit(
181 {
182 name: row.name,
183 enabled: value,
184 },
185 {
186 onSuccess: () => {
187 toast.success(__("Field dependency updated successfully."));
188 },
189 onError: () => {
190 row.enabled = !value;
191 toast.error(__("Failed to update field dependency."));
192 },
193 }
194 );
195}
196</script>
197