fix: Update help center styles, fix routing issues (#9061)

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
Pranav
2024-03-04 09:57:14 -08:00
committed by GitHub
parent a9d42e8c7e
commit b4d37fa16b
16 changed files with 163 additions and 218 deletions

View File

@@ -2,7 +2,7 @@
<div class="empty-state py-16 px-1 ml-0 mr-0">
<h3
v-if="title"
class="text-slate-700 dark:text-slate-200 block text-center w-full text-4xl font-thin"
class="text-slate-700 dark:text-slate-200 block text-center w-full text-xl font-thin"
>
{{ title }}
</h3>
@@ -15,6 +15,7 @@
<slot />
</div>
</template>
<script>
export default {
props: {

View File

@@ -1,7 +1,7 @@
<template>
<footer
v-if="isFooterVisible"
class="bg-white dark:bg-slate-800 h-[60px] border-t border-solid border-slate-75 dark:border-slate-700/50 flex items-center justify-between py-0 px-4"
class="bg-white dark:bg-slate-800 h-12 border-t border-solid border-slate-75 dark:border-slate-700/50 flex items-center justify-between px-6"
>
<div class="left-aligned-wrap">
<div class="text-xs text-slate-600 dark:text-slate-200">

View File

@@ -1,5 +1,9 @@
<template>
<div class="menu" role="button" @click.stop="$emit('click')">
<div
class="menu text-slate-800 dark:text-slate-100"
role="button"
@click.stop="$emit('click')"
>
<fluent-icon
v-if="variant === 'icon' && option.icon"
:icon="option.icon"
@@ -48,6 +52,7 @@ export default {
.menu {
width: calc(var(--space-mega) * 2);
@apply flex items-center flex-nowrap p-1 rounded-sm overflow-hidden cursor-pointer;
.menu-label {
@apply my-0 mx-2 text-xs flex-shrink-0;
}

View File

@@ -1,6 +1,6 @@
<template>
<div
class="menu-with-submenu min-width-calc w-full p-1 flex items-center h-7 rounded-md relative bg-white dark:bg-slate-700 justify-between hover:bg-woot-75 cursor-pointer dark:hover:bg-slate-800"
class="text-slate-800 dark:text-slate-100 menu-with-submenu min-width-calc w-full p-1 flex items-center h-7 rounded-md relative bg-white dark:bg-slate-700 justify-between hover:bg-woot-75 cursor-pointer dark:hover:bg-slate-800"
:class="!subMenuAvailable ? 'opacity-50 cursor-not-allowed' : ''"
>
<div class="flex items-center">

View File

@@ -319,7 +319,7 @@
"HEADERS": {
"TITLE": "Title",
"CATEGORY": "Category",
"READ_COUNT": "Read count",
"READ_COUNT": "Views",
"STATUS": "Status",
"LAST_EDITED": "Last edited"
},

View File

@@ -81,7 +81,7 @@ export default {
<style lang="scss" scoped>
.edit-article--container {
@apply my-8 mx-auto py-0 px-6 max-w-[56rem] w-full;
@apply my-8 mx-auto py-0 max-w-[56rem] w-full;
}
.article-heading {

View File

@@ -1,12 +1,19 @@
<template>
<div class="article-container--row">
<span class="article-column article-title">
<emoji-or-icon class="icon-grab" icon="grab-handle" />
<div class="article-block">
<div
class="text-slate-700 dark:text-slate-100 last:border-b-0 bg-white dark:bg-slate-900 my-0 -mx-4 grid grid-cols-1 lg:grid-cols-12 gap-4 border-b border-slate-50 dark:border-slate-800 px-6 py-3"
>
<span class="items-start flex gap-2 col-span-6 text-left">
<fluent-icon
v-if="showDragIcon"
size="20"
class="block cursor-move flex-shrink-0 h-4 mt-1 w-4 text-slate-200 dark:text-slate-700 hover:text-slate-400 hover:dark:text-slate-200"
icon="grab-handle"
/>
<div class="flex flex-col truncate">
<router-link :to="articleUrl(id)">
<h6
:title="title"
class="text-base ltr:text-left rtl:text-right text-slate-800 dark:text-slate-100 mb-0 leading-6 h-6 hover:underline overflow-hidden whitespace-nowrap text-ellipsis"
class="text-base ltr:text-left rtl:text-right text-slate-800 dark:text-slate-100 mb-0.5 leading-6 font-medium hover:underline overflow-hidden whitespace-nowrap text-ellipsis"
>
{{ title }}
</h6>
@@ -16,14 +23,14 @@
v-if="author"
:src="author.thumbnail"
:username="author.name"
size="16px"
size="14px"
/>
<div
v-else
v-tooltip.right="
$t('HELP_CENTER.TABLE.COLUMNS.AUTHOR_NOT_AVAILABLE')
"
class="flex items-center justify-center rounded w-4 h-4 bg-woot-100 dark:bg-woot-700"
class="flex items-center justify-center rounded w-3.5 h-3.5 bg-woot-100 dark:bg-woot-700"
>
<fluent-icon
icon="person"
@@ -32,61 +39,64 @@
class="text-woot-300 dark:text-woot-300"
/>
</div>
<span class="font-normal text-slate-500 dark:text-slate-200 text-sm">
<span class="font-normal text-slate-700 dark:text-slate-200 text-sm">
{{ articleAuthorName }}
</span>
</div>
</div>
</span>
<span class="article-column article-category">
<span class="flex items-center col-span-2">
<router-link
class="text-sm button clear link secondary min-w-0 max-w-full"
class="text-sm hover:underline p-0.5 truncate hover:bg-slate-25 hover:rounded-md"
:to="getCategoryRoute(category.slug)"
>
<span
:title="category.name"
class="category-link-content overflow-hidden whitespace-nowrap text-ellipsis"
>
<span :title="category.name">
{{ category.name }}
</span>
</router-link>
</span>
<span class="article-column article-read-count">
<span class="text-sm" :title="formattedViewCount">
{{ readableViewCount }}
<span
class="flex items-center text-xs lg:text-sm"
:title="formattedViewCount"
>
{{ readableViewCount }}
<span class="lg:hidden ml-1">
{{ ` ${$t('HELP_CENTER.TABLE.HEADERS.READ_COUNT')}` }}
</span>
</span>
<span class="article-column article-status">
<div>
<woot-label
:title="status"
size="small"
variant="smooth"
:color-scheme="labelColor"
/>
</div>
<span class="flex items-center capitalize">
<woot-label
class="!mb-0"
:title="status"
size="small"
variant="smooth"
:color-scheme="labelColor"
/>
</span>
<span class="article-column article-last-edited">
<span class="text-sm">
{{ lastUpdatedAt }}
</span>
<span
class="flex items-center justify-end col-span-2 first-letter:uppercase text-slate-700 dark:text-slate-100 text-xs"
>
{{ lastUpdatedAt }}
</span>
</div>
</template>
<script>
import timeMixin from 'dashboard/mixins/time';
import portalMixin from '../mixins/portalMixin';
import { frontendURL } from 'dashboard/helper/URLHelper';
import EmojiOrIcon from '../../../../../shared/components/EmojiOrIcon.vue';
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
export default {
components: {
EmojiOrIcon,
Thumbnail,
},
mixins: [timeMixin, portalMixin],
props: {
showDragIcon: {
type: Boolean,
default: false,
},
id: {
type: Number,
required: true,
@@ -156,65 +166,3 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.article-container--row {
@apply bg-white dark:bg-slate-900 my-0 -mx-4 py-0 px-4 grid grid-cols-8 gap-4 border-b border-slate-50 dark:border-slate-800;
@media (max-width: 1024px) {
@apply grid-cols-7;
}
@media (max-width: 768px) {
@apply grid-cols-6;
}
&.draggable {
span.article-column.article-title {
@apply -ml-2;
.icon-grab {
@apply block cursor-move h-4 mt-1 w-4 text-slate-100 dark:text-slate-700;
&:hover {
@apply text-slate-300 dark:text-slate-200;
}
}
}
}
span.article-column {
@apply text-slate-700 dark:text-slate-100 text-sm font-semibold py-2 px-0 text-left capitalize last:text-right;
&.article-title {
@apply items-start flex gap-2 col-span-4 text-left;
.icon-grab {
@apply hidden;
}
}
// for screen sizes smaller than 1024px
@media (max-width: 63.9375em) {
&.article-read-count {
@apply hidden;
}
}
@media (max-width: 47.9375em) {
&.article-read-count,
&.article-last-edited {
@apply hidden;
}
}
}
.article-block {
@apply min-w-0;
}
}
span {
@apply font-normal text-slate-700 dark:text-slate-100 text-sm pl-0;
}
</style>

View File

@@ -1,38 +1,38 @@
<template>
<div class="w-full">
<div class="flex-1">
<div
class="my-0 py-0 px-4 grid grid-cols-6 z-10 md:grid-cols-7 lg:grid-cols-8 gap-4 border-b border-slate-100 dark:border-slate-700 sticky top-16 bg-white dark:bg-slate-900"
class="hidden lg:grid py-0 px-6 h-12 content-center grid-cols-12 z-10 gap-4 border-b border-slate-50 dark:border-slate-700 sticky top-16 bg-white dark:bg-slate-900"
:class="{ draggable: onCategoryPage }"
>
<div
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 ltr:text-left rtl:text-right col-span-4"
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-left rtl:text-right col-span-6"
>
{{ $t('HELP_CENTER.TABLE.HEADERS.TITLE') }}
</div>
<div
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-left"
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-left rtl:text-right col-span-2"
>
{{ $t('HELP_CENTER.TABLE.HEADERS.CATEGORY') }}
</div>
<div
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-left hidden lg:block"
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-left rtl:text-right hidden lg:block"
>
{{ $t('HELP_CENTER.TABLE.HEADERS.READ_COUNT') }}
</div>
<div
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-left"
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-left rtl:text-right"
>
{{ $t('HELP_CENTER.TABLE.HEADERS.STATUS') }}
</div>
<div
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-right hidden md:block"
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-right rtl:text-left hidden md:block col-span-2"
>
{{ $t('HELP_CENTER.TABLE.HEADERS.LAST_EDITED') }}
</div>
</div>
<draggable
tag="div"
class="border-t-0 px-4"
class="border-t-0 px-4 pb-4"
:disabled="!dragEnabled"
:list="localArticles"
ghost-class="article-ghost-class"
@@ -46,6 +46,7 @@
:class="{ draggable: onCategoryPage }"
:title="article.title"
:author="article.author"
:show-drag-icon="dragEnabled"
:category="article.category"
:views="article.views"
:status="article.status"
@@ -58,7 +59,7 @@
:current-page="currentPage"
:total-count="totalCount"
:page-size="pageSize"
class="dark:bg-slate-900 sticky bottom-0 border-t-0 px-4"
class="dark:bg-slate-900 sticky bottom-0"
@page-change="onPageChange"
/>
</div>
@@ -78,7 +79,7 @@ export default {
props: {
articles: {
type: Array,
default: () => {},
default: () => [],
},
totalCount: {
type: Number,
@@ -95,7 +96,7 @@ export default {
},
data() {
return {
localArticles: [],
localArticles: this.articles || [],
};
},
computed: {
@@ -155,6 +156,10 @@ export default {
};
</script>
<style lang="scss" scoped>
/*
The .article-ghost-class class is maintained as the vueDraggable doesn't allow multiple classes
to be passed in the ghost-class prop.
*/
.article-ghost-class {
@apply opacity-50 bg-slate-50 dark:bg-slate-800;
}

View File

@@ -1,16 +1,14 @@
<template>
<div
class="flex px-4 items-center justify-between w-full h-16 pt-2 sticky top-0 z-50 bg-white dark:bg-slate-900"
class="flex p-6 items-center justify-between w-full h-16 sticky top-0 z-50 bg-white dark:bg-slate-900"
>
<div class="flex items-center">
<woot-sidemenu-icon />
<div class="flex items-center my-0 mx-2">
<h3
class="text-2xl text-slate-800 dark:text-slate-100 font-medium mb-0"
>
<h3 class="text-xl text-slate-800 dark:text-slate-100 font-medium mb-0">
{{ headerTitle }}
</h3>
<span class="text-sm text-slate-600 dark:text-slate-300 my-0 mx-2">{{
<span class="text-sm text-slate-600 dark:text-slate-300 mx-2 mt-0.5">{{
`(${count})`
}}</span>
</div>
@@ -234,6 +232,7 @@ export default {
.dropdown-pane--open {
@apply absolute top-10 right-0 z-50 min-w-[8rem];
}
.dropdown-arrow {
@apply ml-1 rtl:ml-0 rtl:mr-1;
}

View File

@@ -1,5 +1,5 @@
<template>
<div class="flex items-center justify-between w-full h-16 pt-2">
<div class="flex items-center justify-between w-full h-16">
<div class="flex items-center">
<woot-button
icon="chevron-left"
@@ -240,12 +240,15 @@ export default {
@apply absolute right-0;
}
}
.draft-status {
animation: fadeIn 1s;
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}

View File

@@ -1,18 +1,19 @@
<template>
<div
class="h-full overflow-auto w-60 flex flex-col bg-white dark:bg-slate-900 border-r dark:border-slate-700 rtl:border-r-0 rtl:border-l border-slate-50 text-sm px-2 pb-8"
class="h-full overflow-auto w-60 flex flex-col bg-white dark:bg-slate-900 border-r dark:border-slate-700 rtl:border-r-0 rtl:border-l border-slate-50 text-sm"
>
<sidebar-header
:thumbnail-src="thumbnailSrc"
:header-title="headerTitle"
:sub-title="subTitle"
:portal-link="portalLink"
class="px-4"
@open-popover="openPortalPopover"
/>
<transition-group
name="menu-list"
tag="ul"
class="pt-2 list-none ml-0 mb-0"
class="py-2 px-4 list-none ml-0 mb-0"
>
<secondary-nav-item
v-for="menuItem in accessibleMenuItems"
@@ -75,9 +76,6 @@ export default {
default: () => [],
},
},
data() {
return {};
},
computed: {
hasCategory() {
return (

View File

@@ -1,10 +1,12 @@
<template>
<transition name="popover-animation">
<div class="article-settings--container">
<div
class="min-w-[15rem] max-w-[22.5rem] p-6 overflow-y-auto border-l rtl:border-r rtl:border-l-0 border-solid border-slate-50 dark:border-slate-700"
>
<h3 class="text-base text-slate-800 dark:text-slate-100">
{{ $t('HELP_CENTER.ARTICLE_SETTINGS.TITLE') }}
</h3>
<div class="form-wrap">
<div class="mt-4 mb-6">
<label>
{{ $t('HELP_CENTER.ARTICLE_SETTINGS.FORM.CATEGORY.LABEL') }}
<multiselect-dropdown
@@ -64,6 +66,7 @@
{{ $t('HELP_CENTER.ARTICLE_SETTINGS.FORM.META_DESCRIPTION.LABEL') }}
<textarea
v-model="metaDescription"
class="text-sm"
rows="3"
type="text"
:placeholder="
@@ -95,7 +98,7 @@
/>
</label>
</div>
<div class="action-buttons">
<div class="flex flex-col">
<woot-button
icon="archive"
size="small"
@@ -241,35 +244,24 @@ export default {
</script>
<style lang="scss" scoped>
.article-settings--container {
@apply flex-[0.3] min-w-[15rem] max-w-[22.5rem] py-2 pl-4 rtl:pl-0 rtl:pr-4 ml-4 rtl:ml-0 rtl:mr-4 overflow-y-auto border-l rtl:border-r rtl:border-l-0 border-solid border-slate-50 dark:border-slate-700;
.form-wrap {
@apply mt-4 mb-6;
textarea {
@apply text-sm;
}
}
.action-buttons {
@apply flex flex-col;
}
}
::v-deep {
.multiselect {
@apply mb-0;
}
.multiselect__content-wrapper {
@apply hidden;
}
.multiselect--active .multiselect__tags {
padding-right: var(--space-small) !important;
@apply rounded-md;
}
.multiselect__placeholder {
@apply text-slate-300 dark:text-slate-200 pt-2 mb-0;
}
.multiselect__select {
@apply hidden;
}

View File

@@ -5,31 +5,58 @@
Loading...
</div>
</template>
<script>
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { mapGetters } from 'vuex';
export default {
mixins: [uiSettingsMixin],
computed: {
...mapGetters({ portals: 'portals/allPortals' }),
},
mounted() {
const {
last_active_portal_slug: lastActivePortalSlug,
last_active_locale_code: lastActiveLocaleCode,
} = this.uiSettings || {};
this.performRouting();
},
methods: {
isPortalPresent(portalSlug) {
return !!this.portals.find(portal => portal.slug === portalSlug);
},
async performRouting() {
await this.$store.dispatch('portals/index');
this.$nextTick(() => this.routeToLastActivePortal());
},
routeToView(name, params) {
this.$router.replace({ name, params, replace: true });
},
async routeToLastActivePortal() {
// TODO: This method should be written as a navigation guard rather than
// a method in the component.
const {
last_active_portal_slug: lastActivePortalSlug,
last_active_locale_code: lastActiveLocaleCode,
} = this.uiSettings || {};
if (lastActivePortalSlug)
this.$router.replace({
name: 'list_all_locale_articles',
params: {
if (this.isPortalPresent(lastActivePortalSlug)) {
// Check if the last active portal from the user preferences is available in the current
// list of portals. If it is, navigate there. The last active portal is saved in the user's
// UI settings, regardless of the account. Consequently, it's possible that the saved portal
// slug is not available in the current account.
this.routeToView('list_all_locale_articles', {
portalSlug: lastActivePortalSlug,
locale: lastActiveLocaleCode,
},
replace: true,
});
else
this.$router.replace({
name: 'list_all_portals',
replace: true,
});
});
} else if (this.portals.length > 0) {
// If the last active portal is available, check for the exisiting list of portals and
// navigate to the first available portal.
const { slug: portalSlug, meta: { default_locale: locale } = {} } =
this.portals[0];
this.routeToView('list_all_locale_articles', { portalSlug, locale });
} else {
// If no portals are available, navigate to the portal list page to prompt creation.
this.$router.replace({ name: 'list_all_portals', replace: true });
}
},
},
};
</script>

View File

@@ -1,8 +1,8 @@
<template>
<div class="article-container">
<div class="article-container flex w-full overflow-auto">
<div
class="edit-article--container"
:class="{ 'is-sidebar-open': showArticleSettings }"
class="flex-1 flex-shrink-0 overflow-auto px-6"
:class="{ 'flex-grow-1 flex-shrink-0': showArticleSettings }"
>
<edit-article-header
:back-button-label="$t('HELP_CENTER.HEADER.TITLES.ALL_ARTICLES')"
@@ -45,6 +45,7 @@
/>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import EditArticleHeader from '../../components/Header/EditArticleHeader.vue';
@@ -107,7 +108,11 @@ export default {
},
methods: {
onClickGoBack() {
this.$router.push({ name: 'list_all_locale_articles' });
if (window.history.length > 2) {
this.$router.go(-1);
} else {
this.$router.push({ name: 'list_all_locale_articles' });
}
},
fetchArticleDetails() {
this.$store.dispatch('articles/show', {
@@ -210,24 +215,3 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.article-container {
display: flex;
padding: 0 var(--space-normal);
width: 100%;
flex: 1;
overflow: auto;
.edit-article--container {
flex: 1;
flex-shrink: 0;
overflow: auto;
}
.is-sidebar-open {
flex-grow: 1;
flex-shrink: 0;
}
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<div
class="py-0 px-0 w-full max-w-full overflow-auto bg-white dark:bg-slate-900"
class="py-0 px-0 w-full max-w-full overflow-auto bg-white dark:bg-slate-900 flex flex-col"
>
<article-header
:header-title="headerTitle"
@@ -8,31 +8,35 @@
:selected-locale="activeLocaleName"
:all-locales="allowedLocales"
selected-value="Published"
class="border-b border-slate-50 dark:border-slate-700"
@new-article-page="newArticlePage"
@change-locale="onChangeLocale"
/>
<article-table
:articles="articles"
:current-page="Number(meta.currentPage)"
:total-count="Number(meta.count)"
@page-change="onPageChange"
@reorder="onReorder"
/>
<div
v-if="shouldShowLoader"
v-if="isFetching"
class="items-center flex text-base justify-center py-6 px-4 text-slate-600 dark:text-slate-200"
>
<spinner />
<span class="text-slate-600 dark:text-slate-200">{{
$t('HELP_CENTER.TABLE.LOADING_MESSAGE')
}}</span>
<span class="text-slate-600 dark:text-slate-200">
{{ $t('HELP_CENTER.TABLE.LOADING_MESSAGE') }}
</span>
</div>
<empty-state
v-else-if="shouldShowEmptyState"
:title="$t('HELP_CENTER.TABLE.NO_ARTICLES')"
/>
<div v-else class="flex flex-1">
<article-table
:articles="articles"
:current-page="Number(meta.currentPage)"
:total-count="Number(meta.count)"
@page-change="onPageChange"
@reorder="onReorder"
/>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import allLocales from 'shared/constants/locales.js';
@@ -72,9 +76,6 @@ export default {
shouldShowEmptyState() {
return !this.isFetching && !this.articles.length;
},
shouldShowLoader() {
return this.isFetching && !this.articles.length;
},
selectedPortalSlug() {
return this.$route.params.portalSlug;
},

View File

@@ -1,8 +1,8 @@
<template>
<div class="article-container">
<div class="flex flex-1 overflow-auto">
<div
class="new-article--container"
:class="{ 'is-sidebar-open': showArticleSettings }"
class="flex-1 overflow-y-auto flex-shrink-0 px-6"
:class="{ 'flex-grow-1': showArticleSettings }"
>
<edit-article-header
:back-button-label="$t('HELP_CENTER.HEADER.TITLES.ALL_ARTICLES')"
@@ -22,6 +22,7 @@
/>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import EditArticleHeader from 'dashboard/routes/dashboard/helpcenter/components/Header/EditArticleHeader.vue';
@@ -116,22 +117,3 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.article-container {
display: flex;
padding: 0 var(--space-normal);
width: 100%;
flex: 1;
overflow: auto;
}
.new-article--container {
flex: 1;
flex-shrink: 0;
overflow-y: auto;
}
.is-sidebar-open {
flex: 0.7;
flex-grow: 1;
}
</style>