diff --git a/packages/server/folder/folder.schema.js b/packages/server/folder/folder.schema.js
index 8a311f1b..729a9fdb 100644
--- a/packages/server/folder/folder.schema.js
+++ b/packages/server/folder/folder.schema.js
@@ -47,4 +47,6 @@ FolderSchema.virtual('childFolders', {
justOne: false,
});
+FolderSchema.index({ _parentFolder: 1 });
+
module.exports = FolderSchema;
diff --git a/packages/server/group/group.controller.js b/packages/server/group/group.controller.js
index b347c619..06111ad9 100644
--- a/packages/server/group/group.controller.js
+++ b/packages/server/group/group.controller.js
@@ -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
+ });
}
/**
diff --git a/packages/server/mailing/mailing.schema.js b/packages/server/mailing/mailing.schema.js
index 869fc06d..7185633e 100644
--- a/packages/server/mailing/mailing.schema.js
+++ b/packages/server/mailing/mailing.schema.js
@@ -165,6 +165,9 @@ MailingSchema.index({ tags: -1 });
MailingSchema.index({ _company: 1, tags: 1 });
MailingSchema.index({ _company: 1, _parentFolder: 1, updatedAt: -1 });
MailingSchema.index({ _company: 1, _workspace: 1, updatedAt: -1 });
+MailingSchema.index({ _user: 1 });
+MailingSchema.index({ _parentFolder: 1 });
+
MailingSchema.statics.findForApi = async function findForApi(query = {}) {
return this.find(query, { previewHtml: 0, data: 0 });
};
@@ -253,7 +256,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,
@@ -266,8 +271,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,
diff --git a/packages/server/template/template.schema.js b/packages/server/template/template.schema.js
index c5814ae0..99a98012 100644
--- a/packages/server/template/template.schema.js
+++ b/packages/server/template/template.schema.js
@@ -97,58 +97,32 @@ TemplateSchema.plugin(mongooseHidden, {
// })
TemplateSchema.virtual('hasMarkup').get(function () {
- return this.markup != null;
+ return Boolean(this.markup);
});
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
+ const templates = await this.find(query, {
+ id: '$_id',
+ name: 1,
+ description: 1,
+ createdAt: 1,
+ updatedAt: 1,
+ _company: 1,
+ assets: 1,
+ hasMarkup: {
+ $cond: { if: { $gt: ['$markup', null] }, then: true, else: false },
+ },
+ })
.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
- 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;
- });
-};
+ .sort({ name: 1 })
+ .lean();
-// TemplateSchema.virtual('url').get(function() {
-// let userId = this._user && this._user._id ? this._user._id : this._user
-// let userUrl = this._user ? `/users/${userId}` : '/users'
-// let companyId =
-// this._company && this._company._id ? this._company._id : this._company
-// let companyUrl = this._company ? `/companies/${companyId}` : '/companies'
-// // read should be `/companies/${this._company}/wireframes/${this._id}`
-// return {
-// read: `/users/${this._user}/wireframe/${this._id}`,
-// show: `/wireframes/${this._id}`,
-// backTo: this._company ? companyUrl : userUrl,
-// user: userUrl,
-// company: companyUrl,
-// delete: `/wireframes/${this._id}/delete`,
-// removeImages: `/wireframes/${this._id}/remove-images`,
-// markup: `/wireframes/${this._id}/markup`,
-// preview: `/wireframes/${this._id}/preview`,
-// generatePreviews: `/wireframes/${this._id}/generate-previews`,
-// imgCover: this.assets['_full.png']
-// ? `/img/${this.assets['_full.png']}`
-// : false,
-// }
-// })
+ const finalTemplates = templates.map(({ assets, ...template }) => ({
+ ...template,
+ coverImage: JSON.parse(assets)?.['_full.png'] || null,
+ }));
+ return finalTemplates;
+};
module.exports = TemplateSchema;
diff --git a/packages/server/user/user.controller.js b/packages/server/user/user.controller.js
index bd05985c..2b8562df 100644
--- a/packages/server/user/user.controller.js
+++ b/packages/server/user/user.controller.js
@@ -140,20 +140,39 @@ 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 parsedPage = parseInt(page, 10);
+ const parsedLimit = parseInt(limit, 10);
+ const offset = (parsedPage - 1) * parsedLimit;
+
+ 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 })
+ .select('-previewHtml -data')
+ .skip(offset)
+ .limit(parsedLimit),
+ 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: parsedPage,
+ totalPages: Math.ceil(totalItems / parsedLimit), // Calculate the total number of pages
+ });
}
/**
diff --git a/packages/ui/components/group/mailings-tab.vue b/packages/ui/components/group/mailings-tab.vue
index e3756126..934aa369 100644
--- a/packages/ui/components/group/mailings-tab.vue
+++ b/packages/ui/components/group/mailings-tab.vue
@@ -6,24 +6,59 @@ 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 {
+ 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) {
+ this.pagination.page = 1;
+ this.pagination.itemsPerPage = itemsPerPage;
+ },
},
};
@@ -31,7 +66,32 @@ export default {
-
+
+
+
+
diff --git a/packages/ui/components/mailings/admin-table.vue b/packages/ui/components/mailings/admin-table.vue
index 130336fd..93e00b37 100644
--- a/packages/ui/components/mailings/admin-table.vue
+++ b/packages/ui/components/mailings/admin-table.vue
@@ -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() {
@@ -21,11 +22,22 @@ export default {
].filter((column) => !this.hiddenCols.includes(column.value));
},
},
+ methods: {
+ handleItemsPerPageChange(itemsPerPage) {
+ this.$emit('update:items-per-page', itemsPerPage);
+ },
+ },
};
-
+
{{ item.name }}
diff --git a/packages/ui/components/templates/table.vue b/packages/ui/components/templates/table.vue
index d5e4e9f3..abe51c58 100644
--- a/packages/ui/components/templates/table.vue
+++ b/packages/ui/components/templates/table.vue
@@ -52,7 +52,7 @@ export default {
-
+
{{ item.name }}
diff --git a/packages/ui/routes/users/_userId.vue b/packages/ui/routes/users/_userId.vue
index e6b40637..bb4ad5b3 100644
--- a/packages/ui/routes/users/_userId.vue
+++ b/packages/ui/routes/users/_userId.vue
@@ -32,6 +32,14 @@ export default {
mailings: [],
loading: false,
isLoadingMailings: false,
+ pagination: {
+ page: 1,
+ itemsPerPage: 10,
+ itemsLength: 0,
+ pageCount: 0,
+ pageStart: 0,
+ pageStop: 0,
+ },
};
},
head() {
@@ -42,6 +50,10 @@ export default {
return `${this.$tc('global.user', 1)} – ${this.user.name}`;
},
},
+ watch: {
+ 'pagination.page': 'loadMailings',
+ 'pagination.itemsPerPage': 'loadMailings',
+ },
mounted() {
this.loadMailings();
},
@@ -54,17 +66,32 @@ export default {
$axios,
$route: { params },
} = this;
-
- const mailingsResponse = await $axios.$get(
- apiRoutes.usersItemMailings(params)
+ const response = await $axios.$get(
+ apiRoutes.usersItemMailings(params),
+ {
+ params: {
+ page: this.pagination.page,
+ limit: this.pagination.itemsPerPage,
+ },
+ }
);
- this.mailings = mailingsResponse.items;
+ this.mailings = response.items;
+ this.pagination.itemsLength = response.totalItems;
+ this.pagination.pageCount = response.totalPages;
+ this.pagination.pageStart =
+ (this.pagination.page - 1) * this.pagination.itemsPerPage;
+ this.pagination.pageStop =
+ this.pagination.pageStart + this.mailings.length;
} catch (error) {
- console.error(error);
+ console.error('Error fetching mailings for user:', error);
} finally {
this.isLoadingMailings = false;
}
},
+ handleItemsPerPageChange(itemsPerPage) {
+ this.pagination.page = 1;
+ this.pagination.itemsPerPage = itemsPerPage;
+ },
async updateUser() {
this.loading = true;
try {
@@ -133,9 +160,31 @@ export default {
+
+
+