fix: updated users page to update user directly and removed unnecessary code
(cherry picked from commit 364c369199c4204962a55c58bc0980d3942e3e8b)
This commit is contained in:
parent
e3cd126304
commit
1b30b3ebec
@ -21,13 +21,16 @@
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="p-2 group bg-surface-gray-2 hover:bg-surface-gray-3 rounded">
|
<div class="p-2 group bg-surface-gray-2 hover:bg-surface-gray-3 rounded">
|
||||||
<MultiSelectEmailInput
|
<MultiSelectUserInput
|
||||||
|
v-if="users?.data?.crmUsers.length"
|
||||||
class="flex-1"
|
class="flex-1"
|
||||||
inputClass="!bg-surface-gray-2 hover:!bg-surface-gray-3 group-hover:!bg-surface-gray-3"
|
inputClass="!bg-surface-gray-2 hover:!bg-surface-gray-3 group-hover:!bg-surface-gray-3"
|
||||||
:placeholder="__('john@doe.com')"
|
:placeholder="__('john@doe.com')"
|
||||||
v-model="newUsers"
|
v-model="newUsers"
|
||||||
:validate="validateEmail"
|
:validate="validateEmail"
|
||||||
:existingEmails="props.users.data.map((user) => user.name)"
|
:existingEmails="
|
||||||
|
users.data.crmUsers.map((user) => user.name) + ['admin@example.com']
|
||||||
|
"
|
||||||
:error-message="
|
:error-message="
|
||||||
(value) => __('{0} is an invalid email address', [value])
|
(value) => __('{0} is an invalid email address', [value])
|
||||||
"
|
"
|
||||||
@ -57,20 +60,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import MultiSelectEmailInput from '@/components/Controls/MultiSelectEmailInput.vue'
|
import MultiSelectUserInput from '@/components/Controls/MultiSelectUserInput.vue'
|
||||||
import { validateEmail } from '@/utils'
|
import { validateEmail } from '@/utils'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { createResource, toast } from 'frappe-ui'
|
import { createResource, toast } from 'frappe-ui'
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
|
|
||||||
const { users: usersResource, isAdmin, isManager } = usersStore()
|
const { users, isAdmin, isManager } = usersStore()
|
||||||
|
|
||||||
const show = defineModel()
|
const show = defineModel()
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
users: Object,
|
|
||||||
})
|
|
||||||
|
|
||||||
const newUsers = ref([])
|
const newUsers = ref([])
|
||||||
const role = ref('Sales User')
|
const role = ref('Sales User')
|
||||||
|
|
||||||
@ -94,7 +93,7 @@ const roleOptions = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const addNewUser = createResource({
|
const addNewUser = createResource({
|
||||||
url: 'crm.fcrm.doctype.crm_user.crm_user.add_existing_users',
|
url: 'crm.api.user.add_existing_users',
|
||||||
makeParams: () => ({
|
makeParams: () => ({
|
||||||
users: JSON.stringify(newUsers.value),
|
users: JSON.stringify(newUsers.value),
|
||||||
role: role.value,
|
role: role.value,
|
||||||
@ -103,8 +102,7 @@ const addNewUser = createResource({
|
|||||||
toast.success(__('Users added successfully'))
|
toast.success(__('Users added successfully'))
|
||||||
newUsers.value = []
|
newUsers.value = []
|
||||||
show.value = false
|
show.value = false
|
||||||
usersResource.reload()
|
users.reload()
|
||||||
props.users.list.reload()
|
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
toast.error(error.messages[0] || __('Failed to add users'))
|
toast.error(error.messages[0] || __('Failed to add users'))
|
||||||
|
|||||||
@ -1,32 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex h-full flex-col gap-8 p-8 text-ink-gray-9">
|
<div class="flex h-full flex-col gap-6 p-8 text-ink-gray-9">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex justify-between">
|
||||||
<h2 class="flex gap-2 text-xl font-semibold leading-none h-5">
|
<div class="flex flex-col gap-1 w-9/12">
|
||||||
{{ __('Users') }}
|
<h2 class="flex gap-2 text-xl font-semibold leading-none h-5">
|
||||||
</h2>
|
{{ __('Users') }}
|
||||||
<div class="flex item-center space-x-2">
|
</h2>
|
||||||
<FormControl
|
<p class="text-p-base text-ink-gray-6">
|
||||||
v-model="search"
|
{{
|
||||||
:placeholder="'Search'"
|
__(
|
||||||
type="text"
|
'Manage users who can access CRM. Assign roles to control their access. Add/Remove existing users or invite new ones.',
|
||||||
:debounce="300"
|
)
|
||||||
>
|
}}
|
||||||
<template #prefix>
|
</p>
|
||||||
<LucideSearch class="h-4 w-4 text-ink-gray-4" />
|
</div>
|
||||||
</template>
|
<div class="flex item-center space-x-2 w-3/12 justify-end">
|
||||||
</FormControl>
|
|
||||||
<FormControl
|
|
||||||
type="select"
|
|
||||||
:value="currentStatus"
|
|
||||||
:options="[
|
|
||||||
{ label: __('All'), value: 'All' },
|
|
||||||
{ label: __('Active'), value: 'Active' },
|
|
||||||
{ label: __('Inactive'), value: 'Inactive' },
|
|
||||||
]"
|
|
||||||
@change="(e) => changeStatus(e.target.value)"
|
|
||||||
>
|
|
||||||
</FormControl>
|
|
||||||
<Dropdown
|
<Dropdown
|
||||||
:options="[
|
:options="[
|
||||||
{
|
{
|
||||||
@ -48,70 +36,50 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- loading state -->
|
|
||||||
<div v-if="users.loading" class="flex mt-28 justify-between w-full h-full">
|
|
||||||
<Button
|
|
||||||
:loading="users.loading"
|
|
||||||
variant="ghost"
|
|
||||||
class="w-full"
|
|
||||||
size="2xl"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- Empty State -->
|
|
||||||
<div
|
|
||||||
v-if="!users.loading && !users.data?.length"
|
|
||||||
class="flex mt-28 justify-between w-full h-full"
|
|
||||||
>
|
|
||||||
<p class="text-sm text-gray-500 w-full flex justify-center">
|
|
||||||
{{ __('No users found') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<!-- Users List -->
|
<!-- Users List -->
|
||||||
<ul
|
<ul
|
||||||
v-if="!users.loading && Boolean(users.data?.length)"
|
v-if="!users.loading && Boolean(users.data?.crmUsers?.length)"
|
||||||
class="divide-y divide-outline-gray-modals overflow-auto"
|
class="divide-y divide-outline-gray-modals overflow-auto"
|
||||||
>
|
>
|
||||||
<li
|
<template v-for="user in users.data?.crmUsers" :key="user.name">
|
||||||
class="flex items-center justify-between py-2"
|
<li
|
||||||
v-for="user in users.data"
|
v-if="user.name !== 'Administrator'"
|
||||||
:key="user.name"
|
class="flex items-center justify-between py-2"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<Avatar :image="user.image" :label="user.user_name" size="xl" />
|
<Avatar
|
||||||
<div class="flex flex-col gap-1 ml-3">
|
:image="user.user_image"
|
||||||
<div class="flex items-center gap-2 text-base text-ink-gray-9 h-4">
|
:label="user.full_name"
|
||||||
{{ user.user_name }}
|
size="xl"
|
||||||
<Badge
|
/>
|
||||||
v-if="!user.is_active"
|
<div class="flex flex-col gap-1 ml-3">
|
||||||
variant="subtle"
|
<div class="flex items-center text-base text-ink-gray-9 h-4">
|
||||||
theme="gray"
|
{{ user.full_name }}
|
||||||
size="sm"
|
</div>
|
||||||
label="Inactive"
|
<div class="text-base text-ink-gray-5">
|
||||||
/>
|
{{ user.name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-base text-ink-gray-5">
|
|
||||||
{{ user.name }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="flex gap-2 items-center flex-row-reverse">
|
||||||
<div class="flex gap-2 items-center flex-row-reverse">
|
<Dropdown
|
||||||
<Dropdown
|
:options="getMoreOptions(user)"
|
||||||
:options="getMoreOptions(user)"
|
:button="{
|
||||||
:button="{
|
icon: 'more-horizontal',
|
||||||
icon: 'more-horizontal',
|
}"
|
||||||
}"
|
placement="right"
|
||||||
placement="right"
|
/>
|
||||||
/>
|
<Dropdown
|
||||||
<Dropdown
|
:options="getDropdownOptions(user)"
|
||||||
:options="getDropdownOptions(user)"
|
:button="{
|
||||||
:button="{
|
label: roleMap[getUserRole(user.name)],
|
||||||
label: roleMap[getUserRole(user.name)],
|
iconRight: 'chevron-down',
|
||||||
iconRight: 'chevron-down',
|
}"
|
||||||
}"
|
placement="right"
|
||||||
placement="right"
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
</li>
|
||||||
</li>
|
</template>
|
||||||
<!-- Load More Button -->
|
<!-- Load More Button -->
|
||||||
<div
|
<div
|
||||||
v-if="!users.loading && users.hasNextPage"
|
v-if="!users.loading && users.hasNextPage"
|
||||||
@ -126,11 +94,30 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<!-- loading state -->
|
||||||
|
<div v-if="users.loading" class="flex mt-28 justify-between w-full h-full">
|
||||||
|
<Button
|
||||||
|
:loading="users.loading"
|
||||||
|
variant="ghost"
|
||||||
|
class="w-full"
|
||||||
|
size="2xl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Empty State -->
|
||||||
|
<div
|
||||||
|
v-if="!users.loading && !users.data?.crmUsers?.length"
|
||||||
|
class="flex mt-28 justify-between w-full h-full"
|
||||||
|
>
|
||||||
|
<p class="text-sm text-gray-500 w-full flex justify-center">
|
||||||
|
{{ __('No users found') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<AddExistingUserModal
|
<AddExistingUserModal
|
||||||
v-if="showAddExistingModal"
|
v-if="showAddExistingModal"
|
||||||
v-model="showAddExistingModal"
|
v-model="showAddExistingModal"
|
||||||
:users="users"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -139,31 +126,13 @@ import LucideCheck from '~icons/lucide/check'
|
|||||||
import AddExistingUserModal from '@/components/Modals/AddExistingUserModal.vue'
|
import AddExistingUserModal from '@/components/Modals/AddExistingUserModal.vue'
|
||||||
import { activeSettingsPage } from '@/composables/settings'
|
import { activeSettingsPage } from '@/composables/settings'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import {
|
import { Avatar, toast, call } from 'frappe-ui'
|
||||||
Avatar,
|
import { ref, h } from 'vue'
|
||||||
Badge,
|
|
||||||
createListResource,
|
|
||||||
FormControl,
|
|
||||||
toast,
|
|
||||||
call,
|
|
||||||
} from 'frappe-ui'
|
|
||||||
import { ref, h, watch, onMounted } from 'vue'
|
|
||||||
|
|
||||||
const { users: usersResource, getUserRole, isAdmin, isManager } = usersStore()
|
const { users, getUserRole, isAdmin, isManager } = usersStore()
|
||||||
|
|
||||||
const showAddExistingModal = ref(false)
|
const showAddExistingModal = ref(false)
|
||||||
|
|
||||||
const users = createListResource({
|
|
||||||
doctype: 'CRM User',
|
|
||||||
cache: 'CRM Users',
|
|
||||||
fields: ['name', 'image', 'is_active', 'user_name'],
|
|
||||||
filters: { is_active: ['=', 1] },
|
|
||||||
auto: true,
|
|
||||||
start: 0,
|
|
||||||
pageLength: 20,
|
|
||||||
orderBy: 'creation desc',
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleMap = {
|
const roleMap = {
|
||||||
'System Manager': __('Admin'),
|
'System Manager': __('Admin'),
|
||||||
'Sales Manager': __('Manager'),
|
'Sales Manager': __('Manager'),
|
||||||
@ -173,20 +142,13 @@ const roleMap = {
|
|||||||
function getMoreOptions(user) {
|
function getMoreOptions(user) {
|
||||||
let options = [
|
let options = [
|
||||||
{
|
{
|
||||||
label: __('Activate'),
|
label: __('Remove'),
|
||||||
icon: 'check-circle',
|
icon: 'trash-2',
|
||||||
onClick: () => updateStatus(user, true),
|
onClick: () => removeUser(user, true),
|
||||||
condition: () => !user.is_active,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: __('Deactivate'),
|
|
||||||
icon: 'x-circle',
|
|
||||||
onClick: () => updateStatus(user, false),
|
|
||||||
condition: () => user.is_active,
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
return options.filter((option) => option.condition())
|
return options.filter((option) => option.condition?.() || true)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDropdownOptions(user) {
|
function getDropdownOptions(user) {
|
||||||
@ -252,73 +214,25 @@ function RoleOption({ active, role, onClick, selected }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateRole(user, newRole) {
|
function updateRole(user, newRole) {
|
||||||
const currentRole = getUserRole(user.name)
|
if (user.role === newRole) return
|
||||||
if (currentRole === newRole) return
|
|
||||||
|
|
||||||
call('crm.fcrm.doctype.crm_user.crm_user.update_user_role', {
|
call('crm.api.user.update_user_role', {
|
||||||
user: user.name,
|
user: user.name,
|
||||||
new_role: newRole,
|
new_role: newRole,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
toast.success(
|
toast.success(
|
||||||
__('{0} has been granted {1} access', [user.user_name, roleMap[newRole]]),
|
__('{0} has been granted {1} access', [user.full_name, roleMap[newRole]]),
|
||||||
)
|
)
|
||||||
usersResource.reload()
|
|
||||||
users.reload()
|
users.reload()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateStatus(user, status) {
|
function removeUser(user) {
|
||||||
const currentStatus = user.is_active
|
call('crm.api.user.remove_user', {
|
||||||
if (currentStatus === status) return
|
|
||||||
|
|
||||||
call('crm.fcrm.doctype.crm_user.crm_user.update_user_status', {
|
|
||||||
user: user.name,
|
user: user.name,
|
||||||
status,
|
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
toast.success(
|
toast.success(__('User {0} has been removed', [user.full_name]))
|
||||||
__('{0} has been {1}', [
|
|
||||||
user.user_name,
|
|
||||||
status ? 'activated' : 'deactivated',
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
users.reload()
|
users.reload()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentStatus = ref('Active')
|
|
||||||
|
|
||||||
function changeStatus(status) {
|
|
||||||
currentStatus.value = status
|
|
||||||
updateFilters()
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateFilters() {
|
|
||||||
const status = currentStatus.value || 'Active'
|
|
||||||
|
|
||||||
users.filters = {}
|
|
||||||
if (status === 'Active') {
|
|
||||||
users.filters.is_active = ['=', 1]
|
|
||||||
} else if (status === 'Inactive') {
|
|
||||||
users.filters.is_active = ['=', 0]
|
|
||||||
}
|
|
||||||
users.reload()
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => updateFilters())
|
|
||||||
|
|
||||||
const search = ref('')
|
|
||||||
watch(search, (newValue) => {
|
|
||||||
users.filters = {
|
|
||||||
is_active: ['=', 1],
|
|
||||||
user_name: ['like', `%${newValue}%`],
|
|
||||||
}
|
|
||||||
if (!newValue) {
|
|
||||||
users.filters = {
|
|
||||||
is_active: ['=', 1],
|
|
||||||
}
|
|
||||||
users.start = 0
|
|
||||||
users.pageLength = 10
|
|
||||||
}
|
|
||||||
users.reload()
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user