feat: search member
This commit is contained in:
@@ -1,14 +1,30 @@
|
||||
<template>
|
||||
<div class="text-base p-4">
|
||||
<div class="font-semibold mb-1">
|
||||
{{ __(label) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600">
|
||||
{{ __(description) }}
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="font-semibold mb-1">
|
||||
{{ __(label) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600">
|
||||
{{ __(description) }}
|
||||
</div>
|
||||
</div>
|
||||
<FormControl v-model="search" :placeholder="__('Search')" type="text" :debounce="300"/>
|
||||
</div>
|
||||
<div class="my-4">
|
||||
<div v-for="member in members.data" class="grid grid-cols-5 grid-flow-row py-2">
|
||||
<div class="flex items-center space-x-2 col-span-2">
|
||||
|
||||
<!-- Form to add new member -->
|
||||
<div v-if="showForm" class="flex items-center space-x-2 mb-4">
|
||||
<FormControl v-model="member.email" :placeholder="__('Email')" type="email" class="w-full"/>
|
||||
<FormControl v-model="member.first_name" :placeholder="__('First Name')" type="test" class="w-full"/>
|
||||
<Button @click="addMember" variant="subtle">
|
||||
{{ __('Add') }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- Member list -->
|
||||
<div v-for="member in memberList" class="grid grid-cols-5 grid-flow-row py-2 cursor-pointer">
|
||||
<div @click="openProfile(member.username)" class="flex items-center space-x-2 col-span-2">
|
||||
<Avatar :image="member.user_image" :label="member.full_name" size="sm" />
|
||||
<div>
|
||||
{{ member.full_name }}
|
||||
@@ -22,8 +38,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="members.hasNextPage" class="flex justify-center">
|
||||
<Button variant="solid" @click="members.next">
|
||||
<div v-if="hasNextPage" class="flex justify-center">
|
||||
<Button variant="solid" @click="members.reload()">
|
||||
<template #prefix>
|
||||
<component
|
||||
:is="icons['RefreshCw']"
|
||||
@@ -36,9 +52,22 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { createListResource, Avatar, Button, FormControl } from 'frappe-ui'
|
||||
import { createResource, Avatar, Button, FormControl } from 'frappe-ui'
|
||||
import * as icons from 'lucide-vue-next'
|
||||
import { computed } from "vue"
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ref, watch, reactive } from 'vue'
|
||||
|
||||
const router = useRouter()
|
||||
const show = defineModel("show")
|
||||
const search = ref('')
|
||||
const start = ref(0)
|
||||
const memberList = ref([])
|
||||
const hasNextPage = ref(false)
|
||||
const showForm = ref(true)
|
||||
const member = reactive({
|
||||
email: '',
|
||||
first_name: '',
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
label: {
|
||||
@@ -49,18 +78,48 @@ const props = defineProps({
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
},
|
||||
})
|
||||
|
||||
const members = createListResource({
|
||||
const members = createResource({
|
||||
url: "lms.lms.api.get_members",
|
||||
doctype: "User",
|
||||
makeParams: () => {
|
||||
return {
|
||||
search: search.value,
|
||||
start: start.value
|
||||
}
|
||||
},
|
||||
onSuccess(data) {
|
||||
memberList.value = memberList.value.concat(data)
|
||||
start.value = start.value + 20
|
||||
hasNextPage.value = data.length === 20
|
||||
},
|
||||
auto: true,
|
||||
})
|
||||
|
||||
const openProfile = (username) => {
|
||||
show.value = false
|
||||
router.push({
|
||||
name: 'Profile',
|
||||
params: {
|
||||
username: username,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
watch(search, () => {
|
||||
memberList.value = []
|
||||
start.value = 0
|
||||
members.reload()
|
||||
})
|
||||
|
||||
const getRole = (role) => {
|
||||
const map = {
|
||||
'LMS Student': 'Student',
|
||||
'Course Creator': 'Course Instructor',
|
||||
'Course Creator': 'Instructor',
|
||||
'Moderator': 'Moderator',
|
||||
'Batch Evaluator': 'Evaluator'
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<Dialog v-model="show" :options="{ size: '6xl' }">
|
||||
<template #body>
|
||||
<Dialog v-model="show" :options="{ size: '3xl' }">
|
||||
<template #body="{ close }">
|
||||
<div class="flex h-[calc(100vh_-_8rem)]">
|
||||
<div class="flex w-52 shrink-0 flex-col bg-gray-50 p-2">
|
||||
<h1 class="mb-3 px-2 pt-2 text-lg font-semibold">
|
||||
@@ -29,7 +29,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="activeTab && data.doc" class="flex flex-1 flex-col overflow-y-auto">
|
||||
<Members v-if="activeTab.label === 'Members'" :label="activeTab.label" :description="activeTab.description"/>
|
||||
<Members v-if="activeTab.label === 'Members'" :label="activeTab.label" :description="activeTab.description" v-model:show="show"/>
|
||||
<SettingDetails
|
||||
v-else
|
||||
:fields="activeTab.fields"
|
||||
@@ -100,7 +100,7 @@ const tabs = computed(() => {
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Apply rounding on equivalent amount',
|
||||
label: 'Apply rounding on equivalent',
|
||||
name: 'apply_rounding',
|
||||
type: 'checkbox',
|
||||
},
|
||||
@@ -108,62 +108,6 @@ const tabs = computed(() => {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
label: 'Signup',
|
||||
icon: 'LogIn',
|
||||
fields: [
|
||||
{
|
||||
label: 'Show terms of use on signup page',
|
||||
name: 'terms_of_use',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Terms of Use Page',
|
||||
name: 'terms_page',
|
||||
type: 'Link',
|
||||
doctype: 'Web Page',
|
||||
},
|
||||
{
|
||||
label: 'Ask user category during signup',
|
||||
name: 'user_category',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
type: 'Column Break',
|
||||
},
|
||||
{
|
||||
label: 'Show privacy policy on signup page',
|
||||
name: 'privacy_policy',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Privacy Policy Page',
|
||||
name: 'privacy_policy_page',
|
||||
type: 'Link',
|
||||
doctype: 'Web Page',
|
||||
},
|
||||
{
|
||||
type: 'Column Break',
|
||||
},
|
||||
{
|
||||
label: 'Show cookie policy on signup page',
|
||||
name: 'cookie_policy',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Cookie Policy Page',
|
||||
name: 'cookie_policy_page',
|
||||
type: 'Link',
|
||||
doctype: 'Web Page',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
hideLabel: true,
|
||||
@@ -239,6 +183,62 @@ const tabs = computed(() => {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
label: 'Signup',
|
||||
icon: 'LogIn',
|
||||
fields: [
|
||||
{
|
||||
label: 'Show terms of use on signup page',
|
||||
name: 'terms_of_use',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Terms of Use Page',
|
||||
name: 'terms_page',
|
||||
type: 'Link',
|
||||
doctype: 'Web Page',
|
||||
},
|
||||
{
|
||||
label: 'Ask user category during signup',
|
||||
name: 'user_category',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
type: 'Column Break',
|
||||
},
|
||||
{
|
||||
label: 'Show privacy policy on signup page',
|
||||
name: 'privacy_policy',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Privacy Policy Page',
|
||||
name: 'privacy_policy_page',
|
||||
type: 'Link',
|
||||
doctype: 'Web Page',
|
||||
},
|
||||
{
|
||||
type: 'Column Break',
|
||||
},
|
||||
{
|
||||
label: 'Show cookie policy on signup page',
|
||||
name: 'cookie_policy',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Cookie Policy Page',
|
||||
name: 'cookie_policy_page',
|
||||
type: 'Link',
|
||||
doctype: 'Web Page',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
hideLabel: true,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div class="flex flex-col justify-between h-full p-10">
|
||||
<div class="flex space-x-10">
|
||||
<div class="flex flex-col justify-between h-full p-8">
|
||||
<div class="flex space-x-8">
|
||||
<div v-for="(column, index) in columns" :key="index">
|
||||
<div class="flex flex-col space-y-4">
|
||||
<div v-for="field in column" :class="width">
|
||||
<div class="flex flex-col space-y-4 w-60">
|
||||
<div v-for="field in column">
|
||||
<Link
|
||||
v-if="field.type == 'Link'"
|
||||
v-model="field.value"
|
||||
@@ -31,11 +31,9 @@
|
||||
|
||||
<script setup>
|
||||
import { FormControl, Button } from 'frappe-ui'
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
|
||||
let width = ref('w-full')
|
||||
|
||||
const props = defineProps({
|
||||
fields: {
|
||||
type: Array,
|
||||
@@ -71,12 +69,6 @@ const columns = computed(() => {
|
||||
cols.push(currentColumn)
|
||||
}
|
||||
|
||||
if (cols.length == 3) {
|
||||
width.value = 'w-64'
|
||||
} else {
|
||||
width.value = 'w-96'
|
||||
}
|
||||
|
||||
return cols
|
||||
})
|
||||
|
||||
|
||||
@@ -564,11 +564,17 @@ def get_categories(doctype, filters):
|
||||
return categoryOptions
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_members(start=0):
|
||||
members = frappe.get_all("User", filters={
|
||||
"enabled": 1,
|
||||
"name": ["not in", ["Administrator", "Guest"]]
|
||||
}, fields=["name", "full_name", "user_image"],
|
||||
def get_members(start=0, search=""):
|
||||
filters = {
|
||||
"enabled": 1,
|
||||
"name": ["not in", ["Administrator", "Guest"]]
|
||||
}
|
||||
|
||||
if search:
|
||||
filters["full_name"] = ["like", f"%{search}%"]
|
||||
|
||||
print(filters)
|
||||
members = frappe.get_all("User", filters=filters, fields=["name", "full_name", "user_image", "username"],
|
||||
page_length=20, start=start)
|
||||
|
||||
for member in members:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "frappe_lms",
|
||||
"version": "1.0.0",
|
||||
"description": "Easy to use, open-source, Learning Management System",
|
||||
"workspaces1": [
|
||||
"workspaces": [
|
||||
"frappe-ui",
|
||||
"frontend"
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user