Skip to content

Commit

Permalink
refactor: customer and contact list view
Browse files Browse the repository at this point in the history
  • Loading branch information
RitvikSardana committed Dec 9, 2024
1 parent 0011851 commit fd898a7
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 105 deletions.
77 changes: 54 additions & 23 deletions desk/src/components/ListViewBuilder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
>
<QuickFilters />
<div class="flex items-center gap-2">
<Button :label="'Refresh'" @click="reload()" :loading="list.loading">
<Button label="Refresh" @click="reload()" :loading="list.loading">
<template #icon>
<RefreshIcon class="h-4 w-4" />
</template>
Expand All @@ -19,6 +19,7 @@
<!-- List View -->
<slot v-bind="{ list }">
<ListView
v-if="list.data?.data.length > 0"
class="flex-1"
:columns="columns"
:rows="rows"
Expand All @@ -27,8 +28,8 @@
selectable: true,
showTooltip: true,
resizeColumn: false,
onRowClick: (row: Object) => emit('onRowClick', row['name']),
empty_state: props.options?.empty_state || defaultEmptyState,
onRowClick: (row: Object) => emit('rowClick', row['name']),
emptyState,
}"
>
<ListHeader class="sm:mx-5 mx-3">
Expand All @@ -48,23 +49,39 @@
class="truncate text-base"
>
<ListRowItem :item="item" :row="row" :column="column">
<!-- TODO: filters on click of other columns -->
<!-- and not on first column, it should emit the event -->
<div v-if="idx === 0">
{{ item }}
</div>
<div v-else-if="column.type === 'Datetime'">
{{ dayjs.tz(item).fromNow() }}
</div>
<div v-else>
<div v-else class="truncate">
{{ item }}
</div>
</ListRowItem>
</ListRow>
</ListRows>
</ListView>
<!-- <div v-else class="flex h-full items-center justify-center">
<div
class="flex flex-col items-center gap-3 text-xl font-medium text-ink-gray-4"
>
<ContactsIcon class="h-10 w-10" />
<span>{{ __("No {0} Found", [__("Contacts")]) }}</span>
<Button :label="__('Create')" @click="showContactModal = true">
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
</Button>
</div>
</div> -->
</slot>

<!-- List Footer -->
<div class="p-20 border-t sm:px-5 px-3 py-2">
<div
class="p-20 border-t sm:px-5 px-3 py-2"
v-if="list.data?.data.length > 0"
>
<ListFooter
:options="{
rowCount: list?.data?.row_count,
Expand All @@ -80,10 +97,24 @@
"
/>
</div>
<div v-else class="flex h-full items-center justify-center">
<div
class="flex flex-col items-center gap-3 text-xl font-medium text-ink-gray-4"
>
<!-- ICON -->
<component :is="emptyState.icon" class="h-10 w-10" />
<!-- title -->
<span>{{ emptyState.title || "No Data Found" }}</span>
<!-- Button which emits Empty State Action -->
<Button label="Create" @click="emit('emptyStateAction')" variant="subtle">
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
</Button>
</div>
</div>
</template>

<script setup lang="ts">
import { reactive, provide, computed } from "vue";
import { reactive, provide, computed, h } from "vue";
import {
createResource,
ListView,
Expand All @@ -96,46 +127,46 @@ import {
} from "frappe-ui";
import { Filter, SortBy, QuickFilters } from "@/components/view-controls";
import { dayjs } from "@/dayjs";
import PhoneIcon from "./icons/PhoneIcon.vue";
interface P {
options: {
doctype: string;
default_filters?: Record<string, any>;
column_config?: Record<string, any>;
update_list_view?: boolean;
empty_state?: {
defaultFilters?: Record<string, any>;
columnConfig?: Record<string, any>;
emptyState?: {
icon?: HTMLElement | string;
title: string;
description?: string;
button?: {
label: string;
variant: string;
onClick: () => void;
};
};
};
}
interface E {
(event: "emptyStateAction"): void;
(event: "onRowClick", row: any): void;
(event: "rowClick", row: any): void;
}
const props = defineProps<P>();
const emit = defineEmits<E>();
const defaultEmptyState = {
title: "No Data",
description: "No data available",
icon: "",
title: "No Data Found",
};
const defaultParams = reactive({
doctype: props.options.doctype,
filters: props.options.default_filters || {},
order_by: "",
filters: props.options.defaultFilters || {},
order_by: "modified desc",
page_length: 20,
page_length_count: 20,
});
const emptyState = computed(() => {
return props.options?.emptyState || defaultEmptyState;
});
const list = createResource({
url: "helpdesk.api.doc.get_list_data",
params: defaultParams,
Expand Down Expand Up @@ -163,8 +194,8 @@ function handleFetchFromField(column) {
}
function handleColumnConfig(column) {
if (!props.options?.column_config) return column;
const columnConfig = props.options.column_config;
if (!props.options?.columnConfig) return column;
const columnConfig = props.options.columnConfig;
if (!columnConfig.hasOwnProperty(column.key)) return column;
column.prefix = columnConfig[column.key]?.prefix;
Expand Down
16 changes: 0 additions & 16 deletions desk/src/components/view-controls/Filter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -194,25 +194,9 @@ const filters = computed(() => {
let allFilters = list?.params?.filters || list.data?.params?.filters;
if (!allFilters || !filterableFields.data) return new Set();
// remove default filters
// if (props.default_filters) {
// allFilters = removeCommonFilters(props.default_filters, allFilters);
// }
return convertFilters(filterableFields.data, allFilters);
});
function removeCommonFilters(commonFilters, allFilters) {
for (const key in commonFilters) {
if (commonFilters.hasOwnProperty(key) && allFilters.hasOwnProperty(key)) {
if (commonFilters[key] === allFilters[key]) {
delete allFilters[key];
}
}
}
return allFilters;
}
function convertFilters(data, allFilters) {
let f = [];
for (let [key, value] of Object.entries(allFilters)) {
Expand Down
14 changes: 4 additions & 10 deletions desk/src/pages/desk/agent/Agents.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ const isDialogVisible = ref(false);
const options = computed(() => {
return {
doctype: "HD Agent",
default_filters: { is_active: ["=", 1] },
column_config: {
defaultFilters: { is_active: ["=", 1] },
columnConfig: {
agent_name: {
prefix: ({ row }) => {
return h(Avatar, {
Expand All @@ -54,14 +54,8 @@ const options = computed(() => {
},
},
},
empty_state: {
title: "No Data",
description: "No data available",
button: {
label: "New Agent",
variant: "solid",
onClick: () => (isDialogVisible.value = true),
},
emptyState: {
title: "No Agents Found",
},
};
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,12 @@
</Button>
</template>
</LayoutHeader>
<ListView
:columns="columns"
:resource="contacts"
class="mt-2.5"
doctype="Contact"
>
<template #name="{ data }">
<div class="flex items-center gap-2">
<Avatar :label="data.name" :image="data.image" size="sm" />
<div class="line-clamp-1">{{ data.name }}</div>
</div>
</template>
</ListView>
<ListViewBuilder
ref="listViewRef"
:options="options"
@row-click="openContact"
@empty-state-action="isDialogVisible = true"
/>
<NewContactDialog
v-model="isDialogVisible"
@contact-created="handleContactCreated"
Expand All @@ -44,59 +37,47 @@
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { computed, ref, h } from "vue";
import { usePageMeta, Avatar } from "frappe-ui";
import { createListManager } from "@/composables/listManager";
import ListViewBuilder from "@/components/ListViewBuilder.vue";
import NewContactDialog from "@/components/desk/global/NewContactDialog.vue";
import LayoutHeader from "@/components/LayoutHeader.vue";
import { ListView } from "@/components";
import ContactDialog from "./ContactDialog.vue";
import { createToast } from "@/utils";
import { Column } from "@/types";
import { PhoneIcon } from "@/components/icons";
const isDialogVisible = ref(false);
const isContactDialogVisible = ref(false);
const selectedContact = ref(null);
const columns: Column[] = [
{
label: "Name",
key: "name",
width: "w-80",
},
{
label: "Email",
key: "email_id",
width: "w-80",
},
{
label: "Phone",
key: "phone",
width: "w-80",
},
];
const contacts = createListManager({
doctype: "Contact",
fields: ["name", "email_id", "image", "phone"],
auto: true,
transform: (data) => {
for (const d of data) {
d.onClick = () => openContact(d.name);
}
return data;
},
});
usePageMeta(() => {
const listViewRef = ref(null);
const options = computed(() => {
return {
title: "Contacts",
doctype: "Contact",
columnConfig: {
name: {
prefix: ({ row }) => {
return h(Avatar, {
shape: "circle",
image: row.image,
label: row.name,
size: "sm",
});
},
},
mobile_no: {
prefix: PhoneIcon,
},
},
emptyState: {
title: "No Contacts Found",
},
};
});
function handleContactCreated(): void {
isDialogVisible.value = false;
contacts.reload();
listViewRef.value?.reload();
}
function openContact(id: string): void {
Expand All @@ -111,6 +92,11 @@ function handleContactUpdated(): void {
iconClasses: "text-green-500",
});
isContactDialogVisible.value = !isContactDialogVisible.value;
contacts.reload();
listViewRef.value?.reload();
}
usePageMeta(() => {
return {
title: "Contacts",
};
});
</script>
9 changes: 7 additions & 2 deletions desk/src/pages/desk/customer/Customers.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
<ListViewBuilder
ref="listViewRef"
:options="options"
@on-row-click="openCustomer"
@row-click="openCustomer"
@emptyStateAction="isDialogVisible = true"
/>
<NewCustomerDialog
v-model="isDialogVisible"
Expand All @@ -42,6 +43,7 @@ import NewCustomerDialog from "@/components/desk/global/NewCustomerDialog.vue";
import CustomerDialog from "./CustomerDialog.vue";
import LayoutHeader from "@/components/LayoutHeader.vue";
import ListViewBuilder from "@/components/ListViewBuilder.vue";
import PhoneIcon from "@/components/icons/PhoneIcon.vue";
const isDialogVisible = ref(false);
const isCustomerDialogVisible = ref(false);
Expand All @@ -63,7 +65,7 @@ function handleCustomer(updated = false) {
const options = computed(() => {
return {
doctype: "HD Customer",
column_config: {
columnConfig: {
name: {
prefix: ({ row }) => {
return h(Avatar, {
Expand All @@ -75,6 +77,9 @@ const options = computed(() => {
},
},
},
emptyState: {
title: "No Customers Found",
},
};
});
Expand Down
6 changes: 5 additions & 1 deletion desk/src/pages/desk/team/Teams.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@
<ListViewBuilder
:options="{
doctype: 'HD Team',
emptyState: {
title: emptyMessage,
},
}"
@on-row-click="handleRowClick"
@row-click="handleRowClick"
@emptyStateAction="showNewDialog = true"
/>
<Dialog
v-model="showNewDialog"
Expand Down
Loading

0 comments on commit fd898a7

Please sign in to comment.