feat(perms): tell managers once that ticket fields follow permission levels

This commit is contained in:
Sydney
2026-09-30 14:26:34 +05:30
parent 40cd3bea08
commit e7dd63887e
8 changed files with 144 additions and 3 deletions

View File

@@ -20,6 +20,10 @@
v-if="showPermissionNoticeBanner"
:isSidebarCollapsed="isCollapsed"
/>
<TicketFieldPermissionBanner
v-if="showFieldPermissionNoticeBanner"
:isSidebarCollapsed="isCollapsed"
/>
</div>
<SidebarItem
v-if="isOnboardingStepsCompleted && !isCustomerPortal"
@@ -60,6 +64,7 @@
import HDLogo from "@/assets/logos/HDLogo.vue";
import { FrappeCloudIcon, InviteCustomer } from "@/components/icons";
import CustomerPortalPermissionBanner from "@/components/layouts/CustomerPortalPermissionBanner.vue";
import TicketFieldPermissionBanner from "@/components/layouts/TicketFieldPermissionBanner.vue";
import ShortcutsModal from "@/components/modals/ShortcutsModal.vue";
import SettingsModal from "@/components/Settings/SettingsModal.vue";
import { confirmLoginToFrappeCloud } from "@/composables/fc";
@@ -215,6 +220,14 @@ const showPermissionNoticeBanner = computed(() => {
);
});
const showFieldPermissionNoticeBanner = computed(() => {
return (
!isCustomerPortal.value &&
(authStore.isManager || authStore.isAdmin) &&
configStore.showTicketFieldPermissionNotice
);
});
const showOnboardingBanner = computed(() => {
return (
!isCustomerPortal.value &&

View File

@@ -0,0 +1,94 @@
<template>
<div
v-if="!isSidebarCollapsed"
class="flex flex-col gap-3 shadow-sm rounded-6 py-2.5 px-3 bg-surface-elevation-2 text-base"
>
<div class="inline-flex gap-2 text-ink-gray-9">
<LucideShieldAlert class="h-4 w-4 my-0.5 shrink-0" />
<div class="flex flex-col gap-0.5 text-p-sm">
<div class="font-medium">
{{ __("Ticket field permissions") }}
</div>
<div class="text-ink-gray-7">
{{ __("Some ticket fields are now visible to agents only") }}
</div>
</div>
</div>
<Button :label="__('Learn more')" theme="blue" @click="showDialog = true" />
</div>
<Button v-else variant="ghost" @click="showDialog = true">
<LucideShieldAlert class="h-4 w-4 shrink-0" />
</Button>
<Dialog
v-model:open="showDialog"
:title="__('Ticket field permissions have changed')"
>
<template #default>
<div class="flex flex-col gap-3 text-p-base text-ink-gray-7">
<p>
{{
__(
"Ticket fields now use permission levels. Customers can read fields at level 7, and only agents can read fields at level 8."
)
}}
</p>
<p>
{{
__(
"After raising a ticket, customers can only close it, rate it and change the fields marked Editable after creation in the ticket template."
)
}}
</p>
<p>
{{
__(
"Roles you created get level 7 only. Add level 8 in Role Permission Manager for your own agent roles."
)
}}
{{ __("Learn more in the") }}
<a
href="https://docs.frappe.io/helpdesk/customization/perm-levels-in-helpdesk"
target="_blank"
class="underline"
>{{ __("documentation") }}</a
>.
</p>
<p class="text-p-sm text-ink-gray-5">
{{ __("You won't see this notice again.") }}
</p>
</div>
</template>
<template #actions>
<Button
class="w-full"
variant="solid"
:label="__('Got it')"
:loading="dismissResource.loading"
@click="dismissResource.submit()"
/>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { __ } from "@/translation";
import { createResource } from "frappe-ui";
import { ref } from "vue";
import LucideShieldAlert from "~icons/lucide/shield-alert";
defineProps({
isSidebarCollapsed: {
type: Boolean,
default: false,
},
});
const showDialog = ref(false);
const dismissResource = createResource({
url: "helpdesk.api.customer_portal_notice.dismiss_field_permission_notice",
onSuccess: () => {
showDialog.value = false;
},
});
</script>

View File

@@ -39,6 +39,9 @@ export const useConfigStore = defineStore("config", () => {
const showCustomerPortalPermissionNotice = computed(
() => !!parseInt(config.value.show_customer_portal_permission_notice)
);
const showTicketFieldPermissionNotice = computed(
() => !!parseInt(config.value.show_ticket_field_permission_notice)
);
$socket.on("helpdesk:settings-updated", () => configResource.reload());
@@ -56,5 +59,6 @@ export const useConfigStore = defineStore("config", () => {
disableGlobalScopeForSavedReplies,
enableCommentReactions,
showCustomerPortalPermissionNotice,
showTicketFieldPermissionNotice,
};
});

View File

@@ -16,6 +16,7 @@ def get_config():
"disable_saved_replies_global_scope",
"enable_comment_reactions",
"show_customer_portal_permission_notice",
"show_ticket_field_permission_notice",
]
res = frappe.get_value(doctype="HD Settings", fieldname=fields, as_dict=True)

View File

@@ -4,6 +4,7 @@ from frappe.realtime import get_website_room
from helpdesk.utils import agent_manager_only
NOTICE_FLAG = "show_customer_portal_permission_notice"
FIELD_PERMISSION_NOTICE_FLAG = "show_ticket_field_permission_notice"
@frappe.whitelist(methods=["POST"])
@@ -13,6 +14,13 @@ def dismiss_notice() -> None:
disable_notice()
@frappe.whitelist(methods=["POST"])
@agent_manager_only
def dismiss_field_permission_notice() -> None:
"""Permanently dismiss the ticket field permission change notice."""
disable_notice(FIELD_PERMISSION_NOTICE_FLAG)
@frappe.whitelist(methods=["POST"])
@agent_manager_only
def restore_ticket_access() -> None:
@@ -31,10 +39,10 @@ def restore_ticket_access() -> None:
disable_notice()
def disable_notice() -> None:
def disable_notice(flag: str = NOTICE_FLAG) -> None:
"""Turn the flag off and notify connected clients, publishing the same
realtime event HD Settings emits from its on_update."""
frappe.db.set_single_value("HD Settings", NOTICE_FLAG, 0)
frappe.db.set_single_value("HD Settings", flag, 0)
frappe.publish_realtime(
"helpdesk:settings-updated", room=get_website_room(), after_commit=True
)

View File

@@ -63,6 +63,7 @@
"setup_complete",
"initial_helpdesk_name_setup_skipped",
"show_customer_portal_permission_notice",
"show_ticket_field_permission_notice",
"persona_captured",
"column_break_hjfh",
"search_tab",
@@ -157,6 +158,13 @@
"hidden": 1,
"label": "Show Customer Portal Permission Notice"
},
{
"default": "0",
"fieldname": "show_ticket_field_permission_notice",
"fieldtype": "Check",
"hidden": 1,
"label": "Show Ticket Field Permission Notice"
},
{
"default": "0",
"description": "Set once an admin completes the onboarding persona questionnaire",
@@ -557,7 +565,7 @@
"grid_page_length": 50,
"issingle": 1,
"links": [],
"modified": "2026-09-02 18:30:00.000000",
"modified": "2026-09-30 18:00:00.000000",
"modified_by": "Administrator",
"module": "Helpdesk",
"name": "HD Settings",

View File

@@ -62,3 +62,4 @@ helpdesk.patches.relabel_comment_search_index
helpdesk.patches.mark_old_assignment_notifications_read
helpdesk.patches.harden_hd_ticket_field_permissions
helpdesk.patches.backfill_template_field_visible_to
helpdesk.patches.show_ticket_field_permission_notice

View File

@@ -0,0 +1,12 @@
import frappe
def execute():
"""Tell existing sites' managers that ticket fields now follow permission levels.
Fresh sites never run this patch, so they never see the notice.
"""
if frappe.db.count("HD Ticket"):
frappe.db.set_single_value(
"HD Settings", "show_ticket_field_permission_notice", 1
)