Merge pull request #5868 from christianbeeznest/ofaj-22119

Message: Improve mobile messaging layout - refs BT#22119
pull/5889/head
Nicolas Ducoulombier 1 month ago committed by GitHub
commit 512efb0d1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      assets/vue/components/basecomponents/ChamiloIcons.js
  2. 8
      assets/vue/components/message/Form.vue
  3. 6
      assets/vue/components/message/MessageLayout.vue
  4. 331
      assets/vue/views/message/MessageList.vue

@ -123,4 +123,5 @@ export const chamiloIconToClass = {
"event-reminder": "mdi mdi-alarm",
"add-event-reminder": "mdi mdi-alarm-plus",
"session-star": "mdi mdi-star",
"next": "mdi mdi-arrow-right-bold-box",
};

@ -1,6 +1,6 @@
<template>
<div class="grid grid-cols-3 gap-4">
<div class="col-span-2">
<div class="flex flex-col md:flex-row md:space-x-4">
<div class="flex-1">
<BaseInputText
id="item_title"
v-model="messagePayload.title"
@ -37,12 +37,12 @@
:label="t('Send')"
icon="plus"
type="primary"
class="mb-2"
class="mt-4"
@click="onSubmit"
/>
</div>
<div class="space-y-4">
<div class="mt-4 md:mt-0 md:w-1/3">
<p class="text-h6">
<BaseIcon icon="attachment" />
{{ t("Attachments") }}

@ -1,9 +1,9 @@
<template>
<div class="message-layout flex">
<div class="sidebar">
<div class="message-layout flex flex-col md:flex-row">
<div class="sidebar hidden md:block md:w-1/4">
<UserProfileCard />
</div>
<div class="content flex-grow">
<div class="content flex-grow w-full">
<router-view></router-view>
</div>
</div>

@ -1,5 +1,5 @@
<template>
<div class="message-list">
<div class="message-list flex flex-col">
<SectionHeader :title="title">
<BaseButton
icon="email-plus"
@ -46,6 +46,7 @@
icon="inbox"
type="black"
@click="showInbox"
class="w-full md:w-auto"
/>
<BaseButton
@ -53,6 +54,7 @@
icon="email-unread"
type="black"
@click="showUnread"
class="w-full md:w-auto"
/>
<BaseButton
@ -60,6 +62,7 @@
icon="sent"
type="black"
@click="showSent"
class="w-full md:w-auto"
/>
<BaseButton
@ -69,113 +72,199 @@
icon="tag-outline"
type="black"
@click="showInboxByTag(tag)"
class="w-full md:w-auto"
/>
</div>
<DataTable
ref="dtMessages"
v-model:selection="selectedItems"
:loading="isLoading"
:row-class="rowClass"
:rows="initialRowsPerPage"
:rows-per-page-options="[10, 20, 50]"
:total-records="totalItems"
:value="items"
current-page-report-template="{first} to {last} of {totalRecords}"
data-key="@id"
lazy
paginator
paginator-template="RowsPerPageDropdown FirstPageLink PrevPageLink CurrentPageReport NextPageLink LastPageLink"
responsive-layout="scroll"
sort-field="sendDate"
:sort-order="-1"
striped-rows
@page="onPage"
@sort="sortingChanged"
>
<template #header>
<form
class="message-list__searcher-container"
@submit.prevent="onSearch"
<div class="hidden md:block overflow-x-auto">
<DataTable
ref="dtMessages"
v-model:selection="selectedItems"
:loading="isLoading"
:row-class="rowClass"
:rows="initialRowsPerPage"
:rows-per-page-options="[10, 20, 50]"
:total-records="totalItems"
:value="items"
current-page-report-template="{first} to {last} of {totalRecords}"
data-key="@id"
lazy
paginator
paginator-template="RowsPerPageDropdown FirstPageLink PrevPageLink CurrentPageReport NextPageLink LastPageLink"
responsive-layout="scroll"
sort-field="sendDate"
:sort-order="-1"
striped-rows
@page="onPage"
@sort="sortingChanged"
class="w-full table-auto"
>
<template #header>
<form
class="message-list__searcher-container"
@submit.prevent="onSearch"
>
<InputGroup>
<InputText
v-model="searchText"
:placeholder="t('Search')"
type="text"
/>
<BaseButton
icon="search"
type="primary"
is-submit
/>
<BaseButton
icon="close"
type="primary"
@click="onResetSearch"
/>
</InputGroup>
</form>
</template>
<Column selection-mode="multiple" />
<Column :header="showingInbox ? t('From') : t('To')">
<template #body="slotProps">
<BaseAvatarList
v-if="showingInbox && slotProps.data.sender"
:users="[slotProps.data.sender]"
/>
<div
v-else-if="showingInbox && !slotProps.data.sender"
v-text="t('No sender')"
/>
<BaseAvatarList
v-else-if="!showingInbox"
:users="mapReceiverMixToUsers(slotProps.data)"
/>
</template>
</Column>
<Column
:header="t('Title')"
:sortable="true"
field="title"
>
<InputGroup>
<InputText
v-model="searchText"
:placeholder="t('Search')"
type="text"
<template #body="slotProps">
<BaseAppLink
class="text-primary"
:to="{ name: 'MessageShow', query: { id: slotProps.data['@id'] } }"
>
{{ slotProps.data.title }}
</BaseAppLink>
<BaseTag
v-for="tag in findMyReceiver(slotProps.data)?.tags"
:key="tag.id"
:label="tag.tag"
type="info"
/>
</template>
</Column>
<Column
:header="t('Send date')"
:sortable="true"
field="sendDate"
class="truncate w-24 md:w-auto"
>
<template #body="slotProps">
{{ abbreviatedDatetime(slotProps.data.sendDate) }}
</template>
</Column>
<Column :header="t('Actions')">
<template #body="slotProps">
<BaseButton
icon="search"
type="primary"
is-submit
icon="delete"
size="small"
type="danger"
@click="showDlgConfirmDeleteSingle(slotProps)"
/>
</template>
</Column>
</DataTable>
</div>
<!-- List for small screens with pagination -->
<div class="block md:hidden">
<ul class="space-y-4">
<li
v-for="item in paginatedItems"
:key="item.id"
class="bg-white shadow-md rounded-lg p-4"
>
<div class="flex items-center space-x-4">
<BaseAvatarList
v-if="showingInbox && item.sender"
:users="[item.sender]"
/>
<div
v-else-if="showingInbox && !item.sender"
class="text-sm text-gray-600"
v-text="t('No sender')"
/>
<BaseAvatarList
v-else
:users="mapReceiverMixToUsers(item)"
/>
<div class="flex-1">
<div v-if="showingInbox && item.sender" class="font-bold text-lg">
{{ item.sender.name }}
</div>
<div v-if="showingInbox && item.sender" class="text-sm text-gray-600">
{{ item.sender.email }}
</div>
</div>
</div>
<div class="mt-4">
<div class="text-sm font-bold">{{ t('Title') }}:</div>
<BaseAppLink
class="text-base text-blue-600"
:to="{ name: 'MessageShow', query: { id: item['@id'] } }"
>
{{ item.title }}
</BaseAppLink>
</div>
<div v-if="findMyReceiver(item)?.tags.length" class="mt-2">
<div class="text-sm font-bold">{{ t('Tags') }}:</div>
<div>
<BaseTag
v-for="tag in findMyReceiver(item)?.tags"
:key="tag.id"
:label="tag.tag"
type="info"
/>
</div>
</div>
<div class="mt-2">
<div class="text-sm font-bold">{{ t('Send date') }}:</div>
<div class="text-base text-gray-500">{{ abbreviatedDatetime(item.sendDate) }}</div>
</div>
<div class="mt-4 flex space-x-2">
<BaseButton
icon="close"
type="primary"
@click="onResetSearch"
icon="delete"
size="small"
type="danger"
@click="showDlgConfirmDeleteSingle(item)"
/>
</InputGroup>
</form>
</template>
<Column selection-mode="multiple" />
<Column :header="showingInbox ? t('From') : t('To')">
<template #body="slotProps">
<BaseAvatarList
v-if="showingInbox && slotProps.data.sender"
:users="[slotProps.data.sender]"
/>
<div
v-else-if="showingInbox && !slotProps.data.sender"
v-text="t('No sender')"
/>
<BaseAvatarList
v-else-if="!showingInbox"
:users="mapReceiverMixToUsers(slotProps.data)"
/>
</template>
</Column>
<Column
:header="t('Title')"
:sortable="true"
field="title"
>
<template #body="slotProps">
<BaseAppLink
class="text-primary"
:to="{ name: 'MessageShow', query: { id: slotProps.data['@id'] } }"
>
{{ slotProps.data.title }}
</BaseAppLink>
<BaseTag
v-for="tag in findMyReceiver(slotProps.data)?.tags"
:key="tag.id"
:label="tag.tag"
type="info"
/>
</template>
</Column>
<Column
:header="t('Send date')"
:sortable="true"
field="sendDate"
>
<template #body="slotProps">
{{ abbreviatedDatetime(slotProps.data.sendDate) }}
</template>
</Column>
<Column :header="t('Actions')">
<template #body="slotProps">
<BaseButton
icon="delete"
size="small"
type="danger"
@click="showDlgConfirmDeleteSingle(slotProps)"
/>
</template>
</Column>
</DataTable>
</div>
</li>
</ul>
<div class="flex justify-between items-center mt-4">
<BaseButton
:disabled="isPrevDisabled"
@click="prevPage"
icon="back"
type="black"
/>
<span>{{ t('Page') }} {{ currentPage }} {{ t('of') }} {{ totalPages }} ({{ totalItems }} {{ t('messages') }})</span>
<BaseButton
:disabled="isNextDisabled"
@click="nextPage"
icon="next"
type="black"
/>
</div>
</div>
</div>
</template>
@ -310,6 +399,42 @@ const rowClass = (data) => {
let fetchPayload = {}
const rows = ref(10)
const currentPage = ref(1)
const totalPages = computed(() => {
return Math.ceil(totalItems.value / rows.value)
})
const paginatedItems = computed(() => {
if (!items.value.length) {
return []
}
return items.value
})
function nextPage() {
if (currentPage.value < totalPages.value) {
currentPage.value++
fetchPayload.page = currentPage.value
fetchPayload.itemsPerPage = rows.value
loadMessages(false)
}
}
function prevPage() {
if (currentPage.value > 1) {
currentPage.value--
fetchPayload.page = currentPage.value
fetchPayload.itemsPerPage = rows.value
loadMessages(false)
}
}
const isPrevDisabled = computed(() => currentPage.value === 1)
const isNextDisabled = computed(() => currentPage.value === totalPages.value)
function loadMessages(reset = true) {
if (reset) {
store.dispatch("message/resetList")
@ -450,12 +575,14 @@ async function deleteMessage(message) {
}
}
function showDlgConfirmDeleteSingle({ data }) {
function showDlgConfirmDeleteSingle(dataOrItem) {
const item = dataOrItem.data || dataOrItem
confirm.require({
header: t("Confirmation"),
message: t("Are you sure you want to delete %s?", [data.title]),
message: t("Are you sure you want to delete %s?", [item.title]),
accept: async () => {
await deleteMessage(data)
await deleteMessage(item)
},
})
}

Loading…
Cancel
Save