Skip to content

Commit

Permalink
manage pagination of mailings
Browse files Browse the repository at this point in the history
  • Loading branch information
omar-bear committed May 7, 2024
1 parent 440e2b0 commit 21fcd05
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 54 deletions.
34 changes: 29 additions & 5 deletions packages/server/group/group.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,36 @@ async function readProfiles(req, res) {

async function readMailings(req, res) {
const { groupId } = req.params;
const [group, mailings] = await Promise.all([
Groups.findById(groupId).select('_id'),
Mailings.findForApi({ _company: groupId }),
const { page: pageParam = 1, limit: limitParam = 10 } = req.query;
const page = parseInt(pageParam);
let limit = parseInt(limitParam);

let skip = (page - 1) * limit; // Calculate the number of items to skip
if (limit === -1) {
limit = 0;
skip = 0;
}
const group = await Groups.findById(groupId).select('_id');
if (!group) {
throw new NotFound();
}

// Retrieve mailings excluding the 'previewHtml' and 'data' fields and their total count
const [mailings, totalItems] = await Promise.all([
Mailings.find({ _company: groupId })
.select('-previewHtml -data') // Exclude the 'previewHtml' and data field
// in case limit = -1, we want to retrieve all mailings
.skip(skip)
.limit(limit),
Mailings.countDocuments({ _company: groupId }), // Count all mailings for this group
]);
if (!group) throw new NotFound();
res.json({ items: mailings });

res.json({
items: mailings,
totalItems,
currentPage: page,
totalPages: Math.ceil(totalItems / limit), // Calculate the total number of pages
});
}

/**
Expand Down
7 changes: 4 additions & 3 deletions packages/server/mailing/mailing.schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,9 @@ MailingSchema.statics.findForApiWithPagination = async function findForApiWithPa
author: 1,
userId: '$_user',
tags: 1,
previewHtml: '$previewHtml',
hasHtmlPreview: {
$cond: { if: { $gt: ['$previewHtml', null] }, then: true, else: false },
},
_workspace: 1,
espIds: 1,
updatedAt: 1,
Expand All @@ -266,8 +268,7 @@ MailingSchema.statics.findForApiWithPagination = async function findForApiWithPa
const { docs, ...restPaginationProperties } = result;

const convertedResultMailingDocs = docs?.map(
({ previewHtml, wireframe, author, ...doc }) => ({
hasHtmlPreview: !!previewHtml,
({ wireframe, author, ...doc }) => ({
templateName: wireframe,
userName: author,
...doc,
Expand Down
67 changes: 46 additions & 21 deletions packages/server/template/template.schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,28 +102,53 @@ TemplateSchema.virtual('hasMarkup').get(function () {
TemplateSchema.index({ name: 1 });

TemplateSchema.statics.findForApi = async function findForApi(query = {}) {
const templates = await this.find(query)
// we need to keep markup in order for the virtual `hasMarkup` to have the right result
// we also need all assets
.populate({ path: '_company', select: 'id name' })
.sort({ name: 1 });
// change some fields
// • we don't want the markup to be send => remove
// • we don't want all assets => remove
// • BUT we still want the cover image => add
const templates = await this.aggregate([
{ $match: query },
{
$project: {
name: 1,
description: 1,
createdAt: 1,
updatedAt: 1,
group: '$_company',
coverImage: '$assets._full.png',
hasMarkup: {
$cond: { if: '$markup', then: true, else: false },
},
_company: 1, // Include if needed for the populate
},
},
{ $sort: { name: 1 } },
{
$lookup: {
from: 'companies', // Adjust the collection name as needed
localField: '_company',
foreignField: '_id',
as: '_company',
},
},
{ $unwind: '$_company' },
{
$project: {
name: 1,
description: 1,
createdAt: 1,
updatedAt: 1,
group: {
id: '$_company._id',
name: '$_company.name',
},
coverImage: 1,
hasMarkup: 1,
},
},
]);

// Convert ObjectId to string and other cleanup as needed
return templates.map((template) => {
// pick is more performant than omit
const templateRes = _.pick(template.toJSON(), [
'id',
'name',
'description',
'createdAt',
'updatedAt',
'hasMarkup',
'group',
]);
templateRes.coverImage = template.assets['_full.png'];
return templateRes;
template.id = template._id.toString();
delete template._id;
return template;
});
};

Expand Down
26 changes: 20 additions & 6 deletions packages/server/user/user.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,20 +140,34 @@ async function read(req, res) {
* @apiGroup Users
*
* @apiParam {string} userId
* @apiParam {number} [page=1]
* @apiParam {number} [limit=10]
*
* @apiUse mailings
* @apiSuccess {mailings[]} items
*/

async function readMailings(req, res) {
const { userId } = req.params;
const [user, mailings] = await Promise.all([
Users.findById(userId).select({ _id: 1 }),
Mailings.findForApi({ _user: userId }),
const { page = 1, limit = 10 } = req.query;
const offset = (page - 1) * limit;

const user = await Users.findById(userId).select('_id');
if (!user) {
throw new createError.NotFound(); // Ensure this error is properly handled by your error middleware
}

// Retrieve mailings and their total count
const [mailings, totalItems] = await Promise.all([
Mailings.find({ _user: userId }).skip(offset).limit(limit),
Mailings.countDocuments({ _user: userId }), // Count all mailings for this user
]);
if (!user) throw new createError.NotFound();

res.json({ items: mailings });
res.json({
items: mailings,
totalItems,
currentPage: page,
totalPages: Math.ceil(totalItems / limit), // Calculate the total number of pages
});
}

/**
Expand Down
96 changes: 79 additions & 17 deletions packages/ui/components/group/mailings-tab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,94 @@ export default {
name: 'BsGroupMailingsTab',
components: { BsMailingsAdminTable },
data() {
return { mailings: [], loading: false };
return {
mailings: [],
loading: false,
pagination: {
page: 1,
itemsPerPage: 10,
itemsLength: 0,
pageCount: 0,
pageStart: 0,
pageStop: 0,
},
};
},
async mounted() {
const {
$axios,
$route: { params },
} = this;
try {
watch: {
'pagination.page': 'fetchMailings',
'pagination.itemsPerPage': 'fetchMailings',
},
mounted() {
this.fetchMailings();
},
methods: {
async fetchMailings() {
const {
$axios,
$route: { params },
pagination,
} = this;
this.loading = true;
const mailingsResponse = await $axios.$get(
apiRoutes.groupsItemMailings(params)
);
this.mailings = mailingsResponse.items;
} catch (error) {
console.log(error);
} finally {
this.loading = false;
}
try {
console.log('fetchMailings', pagination);
const response = await $axios.$get(
apiRoutes.groupsItemMailings(params),
{
params: { page: pagination.page, limit: pagination.itemsPerPage },
}
);
this.mailings = response.items;
this.pagination.itemsLength = response.totalItems;
this.pagination.pageCount = response.totalPages;
// Calculate the range of items displayed on the current page
this.pagination.pageStart =
(this.pagination.page - 1) * this.pagination.itemsPerPage;
this.pagination.pageStop =
this.pagination.pageStart + this.pagination.itemsPerPage;
} catch (error) {
console.error('Error fetching mailings:', error);
} finally {
this.loading = false;
}
},
handleItemsPerPageChange(itemsPerPage) {
console.log('handleItemsPerPageChange', itemsPerPage);
this.pagination.page = 1;
this.pagination.itemsPerPage = itemsPerPage;
},
},
};
</script>

<template>
<v-card flat tile>
<v-card-text>
<bs-mailings-admin-table :mailings="mailings" :loading="loading" />
<bs-mailings-admin-table
:mailings="mailings"
:loading="loading"
:options="pagination || {}"
:footer-props="{
pagination,
disablePagination: true,
prevIcon: 'none',
nextIcon: 'none',
itemsPerPageOptions: [5, 10, 15, -1],
}"
@update:items-per-page="handleItemsPerPageChange"
/>
<v-card
flat
class="d-flex align-center justify-center mx-auto"
max-width="22rem"
>
<v-pagination
v-if="pagination.itemsLength > 0"
v-model="pagination.page"
:circle="true"
class="my-4 pagination-custom-style"
:length="pagination.pageCount"
/>
</v-card>
</v-card-text>
</v-card>
</template>
14 changes: 13 additions & 1 deletion packages/ui/components/mailings/admin-table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default {
mailings: { type: Array, default: () => [] },
hiddenCols: { type: Array, default: () => [] },
loading: { type: Boolean, default: false },
pagination: { type: Object, default: () => ({}) },
},
computed: {
tablesHeaders() {
Expand All @@ -21,11 +22,22 @@ export default {
].filter((column) => !this.hiddenCols.includes(column.value));
},
},
methods: {
handleItemsPerPageChange(itemsPerPage) {
this.$emit('update:items-per-page', itemsPerPage);
},
},
};
</script>

<template>
<v-data-table :headers="tablesHeaders" :items="mailings" :loading="loading">
<v-data-table
:headers="tablesHeaders"
:items="mailings"
:loading="loading"
v-bind="$attrs"
@update:items-per-page="handleItemsPerPageChange"
>
<template #item.name="{ item }">
<!-- mailings live outside the nuxt application -->
<a :href="`/editor/${item.id}`">{{ item.name }}</a>
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/components/templates/table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export default {
</script>

<template>
<v-data-table :headers="tableHeaders" :items="templates">
<v-data-table :headers="tableHeaders" :items="templates" :loading="loading">
<template #item.name="{ item }">
<nuxt-link :to="`/templates/${item.id}`">
{{ item.name }}
Expand Down

0 comments on commit 21fcd05

Please sign in to comment.