mirror of
https://github.com/nextcloud/spreed.git
synced 2025-12-17 21:12:20 +01:00
feat(Dashboard): add new designs
Signed-off-by: Dorra Jaouad <dorra.jaoued7@gmail.com>
This commit is contained in:
parent
fdcb0fb98d
commit
470dc6673a
9 changed files with 232 additions and 175 deletions
|
|
@ -71,6 +71,12 @@ precedence = "aggregate"
|
|||
SPDX-FileCopyrightText = "2018-2025 Google LLC"
|
||||
SPDX-License-Identifier = "Apache-2.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "img/dashboard/**.png"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "Nextcloud GmbH <https://nextcloud.com/trademarks/>"
|
||||
SPDX-License-Identifier = "LicenseRef-NextcloudTrademarks"
|
||||
|
||||
[[annotations]]
|
||||
path = "img/emojis/**.gif"
|
||||
precedence = "aggregate"
|
||||
|
|
|
|||
BIN
img/dashboard/meetings.png
Normal file
BIN
img/dashboard/meetings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 280 KiB |
BIN
img/dashboard/mentions.png
Normal file
BIN
img/dashboard/mentions.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
BIN
img/dashboard/reminders.png
Normal file
BIN
img/dashboard/reminders.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
BIN
img/dashboard/welcome.png
Normal file
BIN
img/dashboard/welcome.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
117
src/components/Dashboard/DashboardSection.vue
Normal file
117
src/components/Dashboard/DashboardSection.vue
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useIsMobile, useIsSmallMobile } from '@nextcloud/vue/composables/useIsMobile'
|
||||
|
||||
const { wide = false, title = '', subtitle = '', description = '' } = defineProps<{
|
||||
wide?: boolean
|
||||
title?: string
|
||||
subtitle?: string
|
||||
description?: string
|
||||
}>()
|
||||
const isSmallMobile = useIsSmallMobile()
|
||||
const isMobile = useIsMobile()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dashboard-section"
|
||||
:class="{
|
||||
'dashboard-section--wide': wide && !isSmallMobile,
|
||||
'dashboard-section--list': $slots.list,
|
||||
}">
|
||||
<div v-if="!isSmallMobile"
|
||||
class="dashboard-section__bar"
|
||||
:class="{
|
||||
'dashboard-section__bar--narrow': $slots.list || isMobile,
|
||||
gradient: !$slots.image || isMobile,
|
||||
'image-container': $slots.image,
|
||||
}">
|
||||
<slot v-if="!($slots.list || isMobile)" name="image" />
|
||||
</div>
|
||||
<div class="dashboard-section__content">
|
||||
<h3 class="dashboard-section__title">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<span class="dashboard-section__subtitle">{{ subtitle }}</span>
|
||||
<span class="dashboard-section__description">{{ description }}</span>
|
||||
<slot name="list" />
|
||||
<div v-if="$slots.action" class="dashboard-section__action">
|
||||
<slot name="action" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dashboard-section {
|
||||
display: flex;
|
||||
border-radius: var(--border-radius-large);
|
||||
overflow: hidden;
|
||||
border: 2px solid var(--color-border);
|
||||
height: 100%;
|
||||
|
||||
&--wide {
|
||||
flex-direction: row;
|
||||
|
||||
.dashboard-section__content {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: auto;
|
||||
min-height: 0;
|
||||
padding: 0 calc(var(--default-grid-baseline) * 3) calc(var(--default-grid-baseline) * 2) calc(var(--default-grid-baseline) * 5);
|
||||
}
|
||||
|
||||
&__bar {
|
||||
flex: 0 0 200px;
|
||||
|
||||
&.gradient {
|
||||
background: linear-gradient(78deg, var(--color-primary) 60%, var(--color-main-background) 120%);
|
||||
}
|
||||
|
||||
&--narrow {
|
||||
flex: 0 0 10px;
|
||||
}
|
||||
|
||||
// Style for slotted images
|
||||
:deep(img) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
&.image-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__action {
|
||||
padding-block: calc(var(--default-grid-baseline) * 2);
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-block: calc(var(--default-grid-baseline) * 2);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -246,7 +246,7 @@ function handleJoin({ call }: { call: boolean }) {
|
|||
flex-direction: column;
|
||||
flex: 0 0 100%;
|
||||
max-width: 300px;
|
||||
border: 3px solid var(--color-border);
|
||||
border: 2px solid var(--color-border);
|
||||
padding: calc(var(--default-grid-baseline) * 2);
|
||||
border-radius: var(--border-radius-large);
|
||||
background-color: var(--color-main-background);
|
||||
|
|
@ -255,7 +255,7 @@ function handleJoin({ call }: { call: boolean }) {
|
|||
background-color: var(--color-primary-light);
|
||||
|
||||
&:not(.event-card--in-call) {
|
||||
border-color: var(--color-primary-light) !important;
|
||||
border-color: var(--color-primary-element-light-hover) !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,97 +0,0 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useIsMobile } from '@nextcloud/vue/composables/useIsMobile'
|
||||
|
||||
const { wide = false, title = '', subtitle = '', description = '' } = defineProps<{
|
||||
wide?: boolean
|
||||
title?: string
|
||||
subtitle?: string
|
||||
description?: string
|
||||
}>()
|
||||
const isMobile = useIsMobile()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dashboard-section"
|
||||
:class="{
|
||||
'dashboard-section--wide': wide,
|
||||
'dashboard-section--list': $slots.list,
|
||||
}">
|
||||
<div v-if="!isMobile"
|
||||
class="dashboard-section__bar"
|
||||
:class="{ 'dashboard-section__bar--narrow': $slots.list }" />
|
||||
<div class="dashboard-section__content-wrapper">
|
||||
<div class="dashboard-section__content">
|
||||
<h3 class="dashboard-section__title">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<span class="dashboard-section__subtitle">{{ subtitle }}</span>
|
||||
<span class="dashboard-section__description">{{ description }}</span>
|
||||
<slot name="list" />
|
||||
<div v-if="$slots.action" class="dashboard-section__action">
|
||||
<slot name="action" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dashboard-section {
|
||||
display: flex;
|
||||
border-radius: var(--border-radius-large);
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 10px 0 rgba(0,0,0, 0.2);
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
|
||||
&--wide {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
&__content-wrapper {
|
||||
display: flex;
|
||||
flex: auto;
|
||||
height: 96%; // bar is 4%
|
||||
padding: var(--default-grid-baseline);
|
||||
}
|
||||
|
||||
&__content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: inherit;
|
||||
padding: 0 calc(var(--default-grid-baseline) * 3) calc(var(--default-grid-baseline) * 2) calc(var(--default-grid-baseline) * 4);
|
||||
}
|
||||
|
||||
&__bar {
|
||||
background: linear-gradient(100deg, var(--color-primary) 0%, var(--color-main-background) 130%);
|
||||
flex: 0 0 50%;
|
||||
|
||||
&--narrow {
|
||||
flex: 0 0 4%;
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__action {
|
||||
margin-top: auto;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-block: calc(var(--default-grid-baseline) * 2);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -6,8 +6,8 @@
|
|||
import { showError } from '@nextcloud/dialogs'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
import { isRTL, t } from '@nextcloud/l10n'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { useIsMobile } from '@nextcloud/vue/composables/useIsMobile'
|
||||
import { generateUrl, imagePath } from '@nextcloud/router'
|
||||
import { useIsMobile, useIsSmallMobile } from '@nextcloud/vue/composables/useIsMobile'
|
||||
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
|
|
@ -25,8 +25,8 @@ import IconVideoOutline from 'vue-material-design-icons/VideoOutline.vue'
|
|||
import ConversationsListVirtual from '../LeftSidebar/ConversationsList/ConversationsListVirtual.vue'
|
||||
import SearchMessageItem from '../RightSidebar/SearchMessages/SearchMessageItem.vue'
|
||||
import LoadingPlaceholder from '../UIShared/LoadingPlaceholder.vue'
|
||||
import DashboardSection from './DashboardSection.vue'
|
||||
import EventCard from './EventCard.vue'
|
||||
import Section from './Section.vue'
|
||||
import { CONVERSATION } from '../../constants.ts'
|
||||
import { getTalkConfig, hasTalkFeature } from '../../services/CapabilitiesManager.ts'
|
||||
import { EventBus } from '../../services/EventBus.ts'
|
||||
|
|
@ -43,6 +43,7 @@ const canModerateSipDialOut = hasTalkFeature('local', 'sip-support-dialout')
|
|||
const canStartConversations = getTalkConfig('local', 'conversations', 'can-create')
|
||||
const isDirectionRTL = isRTL()
|
||||
const isMobile = useIsMobile()
|
||||
const isSmallMobile = useIsSmallMobile()
|
||||
|
||||
const store = useStore()
|
||||
const router = useRouter()
|
||||
|
|
@ -178,49 +179,53 @@ function scrollEventCards({ direction }: { direction: 'backward' | 'forward' })
|
|||
|
||||
<template>
|
||||
<div class="talk-dashboard-wrapper"
|
||||
:class="{ 'talk-dashboard-wrapper--mobile': isMobile }">
|
||||
<h2 class="talk-dashboard__header">
|
||||
{{ t('spreed', 'Hello, {displayName}', { displayName: actorStore.displayName }, { escape: false }) }}
|
||||
</h2>
|
||||
<div class="talk-dashboard__actions">
|
||||
<NcPopover v-if="canStartConversations"
|
||||
popup-role="dialog">
|
||||
<template #trigger>
|
||||
<NcButton variant="primary">
|
||||
<template #icon>
|
||||
<IconVideoOutline />
|
||||
</template>
|
||||
{{ t('spreed', 'Start meeting now') }}
|
||||
</NcButton>
|
||||
</template>
|
||||
<div role="dialog"
|
||||
aria-labelledby="instant_meeting_dialog"
|
||||
class="instant-meeting__dialog"
|
||||
aria-modal="true">
|
||||
<strong>{{ t('spreed', 'Give your meeting a title') }}</strong>
|
||||
<NcInputField id="room-name"
|
||||
v-model="conversationName"
|
||||
:placeholder="t('spreed', 'Meeting')" />
|
||||
<NcButton variant="primary"
|
||||
@click="startMeeting">
|
||||
{{ t('spreed', 'Create and copy link') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
</NcPopover>
|
||||
<NcButton v-if="canStartConversations"
|
||||
@click="EventBus.emit('new-conversation-dialog:show')">
|
||||
<template #icon>
|
||||
<IconPlus :size="20" />
|
||||
</template>
|
||||
{{ t('spreed', 'Create a new conversation') }}
|
||||
</NcButton>
|
||||
:class="{
|
||||
'talk-dashboard-wrapper--mobile': isMobile,
|
||||
'talk-dashboard-wrapper--small-mobile': isSmallMobile,
|
||||
}">
|
||||
<div class="talk-dashboard__menu">
|
||||
<h2 class="talk-dashboard__header">
|
||||
{{ t('spreed', 'Hello, {displayName}', { displayName: actorStore.displayName }, { escape: false }) }}
|
||||
</h2>
|
||||
<div class="talk-dashboard__actions">
|
||||
<NcPopover v-if="canStartConversations"
|
||||
popup-role="dialog">
|
||||
<template #trigger>
|
||||
<NcButton variant="primary">
|
||||
<template #icon>
|
||||
<IconVideoOutline />
|
||||
</template>
|
||||
{{ t('spreed', 'Start meeting now') }}
|
||||
</NcButton>
|
||||
</template>
|
||||
<div role="dialog"
|
||||
aria-labelledby="instant_meeting_dialog"
|
||||
class="instant-meeting__dialog"
|
||||
aria-modal="true">
|
||||
<strong>{{ t('spreed', 'Give your meeting a title') }}</strong>
|
||||
<NcInputField id="room-name"
|
||||
v-model="conversationName"
|
||||
:placeholder="t('spreed', 'Meeting')" />
|
||||
<NcButton variant="primary"
|
||||
@click="startMeeting">
|
||||
{{ t('spreed', 'Create and copy link') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
</NcPopover>
|
||||
<NcButton v-if="canStartConversations"
|
||||
@click="EventBus.emit('new-conversation-dialog:show')">
|
||||
<template #icon>
|
||||
<IconPlus :size="20" />
|
||||
</template>
|
||||
{{ t('spreed', 'Create a new conversation') }}
|
||||
</NcButton>
|
||||
|
||||
<NcButton @click="EventBus.emit('open-conversations-list:show')">
|
||||
<template #icon>
|
||||
<IconList :size="20" />
|
||||
</template>
|
||||
{{ t('spreed', 'Join open conversations') }}
|
||||
</NcButton>
|
||||
<NcButton @click="EventBus.emit('open-conversations-list:show')">
|
||||
<template #icon>
|
||||
<IconList :size="20" />
|
||||
</template>
|
||||
{{ t('spreed', 'Join open conversations') }}
|
||||
</NcButton>
|
||||
|
||||
<NcButton v-if="canModerateSipDialOut"
|
||||
@click="EventBus.emit('call-phone-dialog:show')">
|
||||
|
|
@ -315,7 +320,7 @@ function scrollEventCards({ direction }: { direction: 'backward' | 'forward' })
|
|||
</DashboardSection>
|
||||
<DashboardSection v-else
|
||||
:title="t('spreed', 'Unread mentions')"
|
||||
:description="t('spreed', 'Messages where you were mentioned will show up here\. You can mention people by typing @ followed by their name')">
|
||||
:description="t('spreed', 'Messages where you were mentioned will show up here. You can mention people by typing @ followed by their name')">
|
||||
<template #image>
|
||||
<img :src="imagePath('spreed', 'dashboard/mentions.png')">
|
||||
</template>
|
||||
|
|
@ -366,25 +371,38 @@ function scrollEventCards({ direction }: { direction: 'backward' | 'forward' })
|
|||
@import '../../assets/variables';
|
||||
|
||||
.talk-dashboard-wrapper {
|
||||
--title-height: calc(var(--default-clickable-area) + var(--default-grid-baseline) * 3); // '.title' height
|
||||
--section-width: 300px;
|
||||
--section-height: 300px;
|
||||
--content-height: calc(100% - var(--title-height));
|
||||
padding: 0 calc(var(--default-grid-baseline) * 3);
|
||||
max-width: calc(100vw - 300px - var(--body-container-margin) * 2); // 300px for the left sidebar and body container margins
|
||||
padding: calc(var(--default-grid-baseline) * 2) calc(var(--default-grid-baseline) * 3);
|
||||
width: calc(100vw - 300px - var(--body-container-margin) * 2); // 300px for the left sidebar and body container margins
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
max-height: 800px;
|
||||
max-width: 1200px;
|
||||
|
||||
&--mobile {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&--small-mobile {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
|
||||
.talk-dashboard__chats {
|
||||
grid-template-columns: 1fr;
|
||||
gap: calc(var(--default-grid-baseline) * 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.talk-dashboard__menu {
|
||||
margin-bottom: calc(var(--default-grid-baseline) * 5);
|
||||
}
|
||||
|
||||
.talk-dashboard__header {
|
||||
font-size: 21px; // NcDialog header font size
|
||||
font-weight: bold;
|
||||
height: 51px; // top bar height
|
||||
line-height: 51px;
|
||||
margin: 0 auto;
|
||||
margin: 0 auto calc(var(--default-grid-baseline) * 2);
|
||||
padding-inline-start: calc(var(--default-clickable-area) + var(--default-grid-baseline)); // navigation button
|
||||
}
|
||||
|
||||
|
|
@ -393,6 +411,15 @@ function scrollEventCards({ direction }: { direction: 'backward' | 'forward' })
|
|||
gap: calc(var(--default-grid-baseline) * 3);
|
||||
padding-block: var(--default-grid-baseline);
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-evenly;
|
||||
|
||||
:deep(.button-vue),
|
||||
:deep(.v-popper--theme-dropdown) {
|
||||
height: var(--header-menu-item-height);
|
||||
border-radius: var(--border-radius-large);
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.button-vue) {
|
||||
padding-inline: calc(var(--default-grid-baseline) * 2) calc(var(--default-grid-baseline) * 4);
|
||||
|
|
@ -400,7 +427,7 @@ function scrollEventCards({ direction }: { direction: 'backward' | 'forward' })
|
|||
}
|
||||
|
||||
.event-section {
|
||||
margin-block: calc(var(--default-grid-baseline) * 8);
|
||||
margin-block-end: calc(var(--default-grid-baseline) * 6);
|
||||
|
||||
&--empty {
|
||||
height: 225px;
|
||||
|
|
@ -460,15 +487,22 @@ function scrollEventCards({ direction }: { direction: 'backward' | 'forward' })
|
|||
inset-inline-start: calc(var(--default-grid-baseline) * 2);
|
||||
}
|
||||
|
||||
.talk-dashboard__chats {
|
||||
.talk-dashboard__items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
min-width: 0;
|
||||
flex-grow: 3;
|
||||
}
|
||||
|
||||
.talk-dashboard__chats {
|
||||
display: grid;
|
||||
gap: calc(var(--default-grid-baseline) * 8);
|
||||
justify-content: space-between;
|
||||
flex-direction: row;
|
||||
height: 300px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
flex-grow: 1;
|
||||
|
||||
&> div {
|
||||
width: calc(50% - calc(var(--default-grid-baseline) * 4));
|
||||
max-height: 360px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -479,33 +513,26 @@ function scrollEventCards({ direction }: { direction: 'backward' | 'forward' })
|
|||
|
||||
&__loading-placeholder {
|
||||
overflow: hidden;
|
||||
height: var(--content-height);
|
||||
}
|
||||
}
|
||||
|
||||
.talk-dashboard__conversations-list {
|
||||
margin: var(--default-grid-baseline) 0;
|
||||
height: var(--content-height);
|
||||
height: 225px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: inherit;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
height: var(--default-clickable-area);
|
||||
margin-block: calc(var(--default-grid-baseline) * 2) var(--default-grid-baseline);
|
||||
margin-inline: var(--default-grid-baseline);
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
margin-block: 0 calc(var(--default-grid-baseline) * 2);
|
||||
}
|
||||
|
||||
.instant-meeting__dialog {
|
||||
padding: 8px;
|
||||
padding: calc(var(--default-grid-baseline) * 2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
gap: var(--default-grid-baseline) ;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
|
@ -514,7 +541,11 @@ function scrollEventCards({ direction }: { direction: 'backward' | 'forward' })
|
|||
.talk-dashboard__actions {
|
||||
:deep(.button-vue),
|
||||
:deep(.v-popper--theme-dropdown) {
|
||||
width: 100%;
|
||||
flex: initial;
|
||||
}
|
||||
|
||||
:deep(.button-vue) {
|
||||
padding-inline-end: calc(var(--default-grid-baseline) * 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue