feat: Add Help Center layout with portal switcher component (#10272)

This commit is contained in:
Sivin Varghese
2024-10-16 01:37:14 +05:30
committed by GitHub
parent 7be1ecaf96
commit 44be3c9eec
3 changed files with 247 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
<script setup>
import { ref } from 'vue';
import { OnClickOutside } from '@vueuse/components';
import PaginationFooter from 'dashboard/components-next/pagination/PaginationFooter.vue';
import Button from 'dashboard/components-next/button/Button.vue';
import PortalSwitcher from 'dashboard/components-next/HelpCenter/PortalSwitcher/PortalSwitcher.vue';
defineProps({
header: {
type: String,
default: 'Chatwoot Help Center',
},
currentPage: {
type: Number,
default: 1,
},
totalItems: {
type: Number,
default: 100,
},
itemsPerPage: {
type: Number,
default: 25,
},
showPaginationFooter: {
type: Boolean,
default: true,
},
});
const emit = defineEmits(['update:currentPage']);
const showPortalSwitcher = ref(false);
const updateCurrentPage = page => {
emit('update:currentPage', page);
};
const togglePortalSwitcher = () => {
showPortalSwitcher.value = !showPortalSwitcher.value;
};
</script>
<template>
<section
class="flex flex-col w-full h-full overflow-hidden bg-white dark:bg-slate-900"
>
<header
class="sticky top-0 z-10 px-6 pb-3 bg-white lg:px-0 dark:bg-slate-900"
>
<div class="w-full max-w-[900px] mx-auto">
<div class="flex items-center justify-start h-20 gap-2">
<span class="text-xl font-medium text-slate-900 dark:text-white">
{{ header }}
</span>
<div class="relative group">
<Button
icon="more-vertical"
variant="ghost"
size="sm"
class="group-hover:bg-slate-100 dark:group-hover:bg-slate-800"
@click="togglePortalSwitcher"
/>
<OnClickOutside @trigger="showPortalSwitcher = false">
<PortalSwitcher
v-if="showPortalSwitcher"
class="absolute left-0 top-9"
/>
</OnClickOutside>
</div>
</div>
<slot name="header-actions" />
</div>
</header>
<main class="flex-1 px-6 overflow-y-auto lg:px-0">
<div class="w-full max-w-[900px] mx-auto py-3">
<slot name="content" />
</div>
</main>
<footer
v-if="showPaginationFooter"
class="sticky bottom-0 z-10 px-4 pt-3 pb-4 bg-white dark:bg-slate-900"
>
<PaginationFooter
:current-page="currentPage"
:total-items="totalItems"
:items-per-page="itemsPerPage"
@update:current-page="updateCurrentPage"
/>
</footer>
</section>
</template>

View File

@@ -0,0 +1,44 @@
<script setup>
import PortalSwitcher from './PortalSwitcher.vue';
const portals = [
{
id: 1,
name: 'Chatwoot Help Center',
articles: 67,
domain: 'chatwoot.help',
slug: 'help-center',
},
{
id: 2,
name: 'Chatwoot Handbook',
articles: 42,
domain: 'chatwoot.help',
slug: 'handbook',
},
{
id: 3,
name: 'Developer Documentation',
articles: 89,
domain: 'dev.chatwoot.com',
slug: 'docs',
},
];
</script>
<template>
<Story
title="Components/HelpCenter/PortalSwitcher"
:layout="{ type: 'grid', width: '510px' }"
>
<Variant title="Portal Switcher">
<div class="h-[500px] p-4 bg-slate-100 dark:bg-slate-900">
<PortalSwitcher
:portals="portals"
header="Choose a Portal"
description="Select from available help center portals"
/>
</div>
</Variant>
</Story>
</template>

View File

@@ -0,0 +1,111 @@
<script setup>
import { ref } from 'vue';
import Button from 'dashboard/components-next/button/Button.vue';
defineProps({
portals: {
type: Array,
default: () => [
{
id: 1,
name: 'Chatwoot Help Center',
articles: 67,
domain: 'chatwoot.help',
slug: 'help-center',
},
{
id: 2,
name: 'Chatwoot Handbook',
articles: 42,
domain: 'chatwoot.help',
slug: 'handbook',
},
],
},
header: {
type: String,
default: 'Portals',
},
description: {
type: String,
default: 'Create and manage multiple portals',
},
});
const selectedPortal = ref(1);
const handlePortalChange = id => {
selectedPortal.value = id;
};
</script>
<!-- TODO: Add i18n -->
<!-- eslint-disable vue/no-bare-strings-in-template -->
<template>
<div
class="pt-5 pb-3 bg-white z-50 dark:bg-slate-800 absolute w-[440px] rounded-xl shadow-md flex flex-col gap-4"
>
<div class="flex items-center justify-between gap-4 px-6 pb-2">
<div class="flex flex-col gap-1">
<h2 class="text-base font-medium text-slate-900 dark:text-slate-50">
{{ header }}
</h2>
<p class="text-sm text-slate-600 dark:text-slate-300">
{{ description }}
</p>
</div>
<Button label="New portal" variant="secondary" icon="add" size="sm" />
</div>
<div v-if="portals.length > 0" class="flex flex-col gap-3">
<template v-for="(portal, index) in portals" :key="portal.id">
<div class="flex flex-col gap-2 px-6 py-2">
<div class="flex items-center justify-between">
<div class="flex items-center">
<input
:id="portal.id"
v-model="selectedPortal"
type="radio"
:value="portal.id"
class="mr-3"
@change="handlePortalChange(portal.id)"
/>
<label
:for="portal.id"
class="text-sm font-medium text-slate-900 dark:text-slate-100"
>
{{ portal.name }}
</label>
</div>
<div class="w-4 h-4 rounded-full bg-slate-100 dark:bg-slate-700" />
</div>
<div class="inline-flex items-center gap-2 py-1 text-sm">
<span class="text-slate-600 dark:text-slate-400">
articles:
<span class="text-slate-800 dark:text-slate-200">
{{ portal.articles }}
</span>
</span>
<div class="w-px h-3 bg-slate-50 dark:bg-slate-700" />
<span class="text-slate-600 dark:text-slate-400">
domain:
<span class="text-slate-800 dark:text-slate-200">
{{ portal.domain }}
</span>
</span>
<div class="w-px h-3 bg-slate-50 dark:bg-slate-700" />
<span class="text-slate-600 dark:text-slate-400">
slug:
<span class="text-slate-800 dark:text-slate-200">
{{ portal.slug }}
</span>
</span>
</div>
</div>
<div
v-if="index < portals.length - 1 && portals.length > 1"
class="w-full h-px bg-slate-50 dark:bg-slate-700/50"
/>
</template>
</div>
</div>
</template>