feat: add existing users
This commit is contained in:
parent
08bab927a2
commit
6873c6db4e
@ -21,6 +21,26 @@ class CRMUser(Document):
|
|||||||
self.image = user.user_image
|
self.image = user.user_image
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def add_existing_users(users, role='Sales User'):
|
||||||
|
"""
|
||||||
|
Add existing users to the CRM User doctype.
|
||||||
|
:param users: List of user names to be added
|
||||||
|
"""
|
||||||
|
frappe.only_for(["System Manager", "Sales Manager"])
|
||||||
|
users = frappe.parse_json(users)
|
||||||
|
|
||||||
|
for user in users:
|
||||||
|
if not frappe.db.exists("CRM User", {"user": user}):
|
||||||
|
new_user = frappe.new_doc("CRM User")
|
||||||
|
new_user.user = user
|
||||||
|
new_user.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
update_user_role(user, role)
|
||||||
|
else:
|
||||||
|
frappe.throw(f"User {user} already exists")
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_user_role(user, new_role):
|
def update_user_role(user, new_role):
|
||||||
"""
|
"""
|
||||||
@ -29,7 +49,7 @@ def update_user_role(user, new_role):
|
|||||||
:param new_role: The new role to assign (Sales Manager or Sales User)
|
:param new_role: The new role to assign (Sales Manager or Sales User)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
frappe.only_for(["System Manager", "System Manager"])
|
frappe.only_for(["System Manager", "Sales Manager"])
|
||||||
|
|
||||||
if new_role not in ["System Manager", "Sales Manager", "Sales User"]:
|
if new_role not in ["System Manager", "Sales Manager", "Sales User"]:
|
||||||
frappe.throw("Cannot assign this role")
|
frappe.throw("Cannot assign this role")
|
||||||
@ -55,6 +75,6 @@ def update_user_status(user, status):
|
|||||||
:param user: The name of the user
|
:param user: The name of the user
|
||||||
:param status: The status to set (1 for active, 0 for inactive)
|
:param status: The status to set (1 for active, 0 for inactive)
|
||||||
"""
|
"""
|
||||||
frappe.only_for("Sales Manager")
|
frappe.only_for(["System Manager", "Sales Manager"])
|
||||||
|
|
||||||
frappe.db.set_value("CRM User", user, "is_active", status)
|
frappe.db.set_value("CRM User", user, "is_active", status)
|
||||||
|
|||||||
1
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
@ -12,6 +12,7 @@ declare module 'vue' {
|
|||||||
Activities: typeof import('./src/components/Activities/Activities.vue')['default']
|
Activities: typeof import('./src/components/Activities/Activities.vue')['default']
|
||||||
ActivityHeader: typeof import('./src/components/Activities/ActivityHeader.vue')['default']
|
ActivityHeader: typeof import('./src/components/Activities/ActivityHeader.vue')['default']
|
||||||
ActivityIcon: typeof import('./src/components/Icons/ActivityIcon.vue')['default']
|
ActivityIcon: typeof import('./src/components/Icons/ActivityIcon.vue')['default']
|
||||||
|
AddExistingUserModal: typeof import('./src/components/Modals/AddExistingUserModal.vue')['default']
|
||||||
AddressIcon: typeof import('./src/components/Icons/AddressIcon.vue')['default']
|
AddressIcon: typeof import('./src/components/Icons/AddressIcon.vue')['default']
|
||||||
AddressModal: typeof import('./src/components/Modals/AddressModal.vue')['default']
|
AddressModal: typeof import('./src/components/Modals/AddressModal.vue')['default']
|
||||||
Agents: typeof import('./src/components/Settings/Agents.vue')['default']
|
Agents: typeof import('./src/components/Settings/Agents.vue')['default']
|
||||||
|
|||||||
113
frontend/src/components/Modals/AddExistingUserModal.vue
Normal file
113
frontend/src/components/Modals/AddExistingUserModal.vue
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog
|
||||||
|
v-model="show"
|
||||||
|
:options="{ title: __('Add Existing User') }"
|
||||||
|
@close="show = false"
|
||||||
|
>
|
||||||
|
<template #body-content>
|
||||||
|
<div class="flex gap-1 border rounded mb-4 p-2 text-ink-gray-5">
|
||||||
|
<FeatherIcon name="info" class="size-3.5" />
|
||||||
|
<p class="text-sm">
|
||||||
|
{{
|
||||||
|
__(
|
||||||
|
'Add existing system users to this CRM. Assign them a role to grant access with their current credentials.',
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="block text-xs text-ink-gray-5 mb-1.5">
|
||||||
|
{{ __('Users') }}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="p-2 group bg-surface-gray-2 hover:bg-surface-gray-3 rounded">
|
||||||
|
<MultiSelectEmailInput
|
||||||
|
class="flex-1"
|
||||||
|
inputClass="!bg-surface-gray-2 hover:!bg-surface-gray-3 group-hover:!bg-surface-gray-3"
|
||||||
|
:placeholder="__('john@doe.com')"
|
||||||
|
v-model="newUsers"
|
||||||
|
:validate="validateEmail"
|
||||||
|
:existingEmails="props.users.data.map((user) => user.name)"
|
||||||
|
:error-message="
|
||||||
|
(value) => __('{0} is an invalid email address', [value])
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<FormControl
|
||||||
|
type="select"
|
||||||
|
class="mt-4"
|
||||||
|
v-model="role"
|
||||||
|
:label="__('Role')"
|
||||||
|
:options="roleOptions"
|
||||||
|
:description="description"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #actions>
|
||||||
|
<div class="flex justify-end gap-2">
|
||||||
|
<Button
|
||||||
|
variant="solid"
|
||||||
|
:label="__('Add')"
|
||||||
|
:disabled="!newUsers.length"
|
||||||
|
@click="addNewUser.submit()"
|
||||||
|
:loading="addNewUser.loading"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import MultiSelectEmailInput from '@/components/Controls/MultiSelectEmailInput.vue'
|
||||||
|
import { validateEmail } from '@/utils'
|
||||||
|
import { usersStore } from '@/stores/users'
|
||||||
|
import { createResource, toast } from 'frappe-ui'
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
|
||||||
|
const { users: usersResource, isAdmin, isManager } = usersStore()
|
||||||
|
|
||||||
|
const show = defineModel()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
users: Object,
|
||||||
|
})
|
||||||
|
|
||||||
|
const newUsers = ref([])
|
||||||
|
const role = ref('Sales User')
|
||||||
|
|
||||||
|
const description = computed(() => {
|
||||||
|
return {
|
||||||
|
'System Manager':
|
||||||
|
'Can manage all aspects of the CRM, including user management, customizations and settings.',
|
||||||
|
'Sales Manager':
|
||||||
|
'Can manage and invite new users, and create public & private views (reports).',
|
||||||
|
'Sales User':
|
||||||
|
'Can work with leads and deals and create private views (reports).',
|
||||||
|
}[role.value]
|
||||||
|
})
|
||||||
|
|
||||||
|
const roleOptions = computed(() => {
|
||||||
|
return [
|
||||||
|
{ value: 'Sales User', label: __('Sales User') },
|
||||||
|
...(isManager() ? [{ value: 'Sales Manager', label: __('Manager') }] : []),
|
||||||
|
...(isAdmin() ? [{ value: 'System Manager', label: __('Admin') }] : []),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const addNewUser = createResource({
|
||||||
|
url: 'crm.fcrm.doctype.crm_user.crm_user.add_existing_users',
|
||||||
|
makeParams: () => ({
|
||||||
|
users: JSON.stringify(newUsers.value),
|
||||||
|
role: role.value,
|
||||||
|
}),
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success(__('Users added successfully'))
|
||||||
|
newUsers.value = []
|
||||||
|
show.value = false
|
||||||
|
usersResource.reload()
|
||||||
|
props.users.list.reload()
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(error.messages[0] || __('Failed to add users'))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@ -44,7 +44,6 @@
|
|||||||
import WhatsAppIcon from '@/components/Icons/WhatsAppIcon.vue'
|
import WhatsAppIcon from '@/components/Icons/WhatsAppIcon.vue'
|
||||||
import ERPNextIcon from '@/components/Icons/ERPNextIcon.vue'
|
import ERPNextIcon from '@/components/Icons/ERPNextIcon.vue'
|
||||||
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
|
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
|
||||||
import InviteIcon from '@/components/Icons/InviteIcon.vue'
|
|
||||||
import Email2Icon from '@/components/Icons/Email2Icon.vue'
|
import Email2Icon from '@/components/Icons/Email2Icon.vue'
|
||||||
import Users from '@/components/Settings/Users.vue'
|
import Users from '@/components/Settings/Users.vue'
|
||||||
import GeneralSettings from '@/components/Settings/GeneralSettings.vue'
|
import GeneralSettings from '@/components/Settings/GeneralSettings.vue'
|
||||||
@ -97,7 +96,7 @@ const tabs = computed(() => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: __('Invite User'),
|
label: __('Invite User'),
|
||||||
icon: InviteIcon,
|
icon: 'user-plus',
|
||||||
component: markRaw(InviteUserPage),
|
component: markRaw(InviteUserPage),
|
||||||
condition: () => isManager(),
|
condition: () => isManager(),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -127,10 +127,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<AddExistingUserModal
|
||||||
|
v-if="showAddExistingModal"
|
||||||
|
v-model="showAddExistingModal"
|
||||||
|
:users="users"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import LucideCheck from '~icons/lucide/check'
|
import LucideCheck from '~icons/lucide/check'
|
||||||
|
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 {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user