Skip to content

Commit 44eafe4

Browse files
authored
Merge pull request #2389 from frappe/mergify/bp/main-hotfix/pr-2367
feat: add empty states to settings (backport #2367)
2 parents dbbf09c + 8f63116 commit 44eafe4

27 files changed

Lines changed: 195 additions & 68 deletions

frontend/src/App.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ import { computed, onUnmounted, ref } from 'vue'
1414
import { useScreenSize } from './utils/composables'
1515
import { useSettings } from '@/stores/settings'
1616
import { useRouter } from 'vue-router'
17-
import DesktopLayout from './components/DesktopLayout.vue'
18-
import MobileLayout from './components/MobileLayout.vue'
19-
import NoSidebarLayout from './components/NoSidebarLayout.vue'
17+
import DesktopLayout from './components/Layouts/DesktopLayout.vue'
18+
import MobileLayout from './components/Layouts/MobileLayout.vue'
19+
import NoSidebarLayout from './components/Layouts/NoSidebarLayout.vue'
2020
import InstallPrompt from './components/InstallPrompt.vue'
2121
2222
const { isMobile } = useScreenSize()

frontend/src/components/EmptyState.vue

Lines changed: 0 additions & 22 deletions
This file was deleted.
File renamed without changes.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<template>
2+
<div
3+
class="flex min-h-[60vh] w-full flex-col items-center justify-center gap-3 px-4"
4+
>
5+
<component :is="icon" class="size-7.5 stroke-1 text-ink-gray-5" />
6+
<div class="flex flex-col items-center gap-1" :class="widthClass">
7+
<span class="text-lg font-medium text-ink-gray-8 text-center">
8+
{{ computedTitle }}
9+
</span>
10+
<span class="text-center text-p-base text-ink-gray-6">
11+
{{ computedDescription }}
12+
</span>
13+
</div>
14+
</div>
15+
</template>
16+
<script setup lang="ts">
17+
import { GraduationCap } from 'lucide-vue-next'
18+
import type { Component } from 'vue'
19+
import { computed } from 'vue'
20+
21+
const props = withDefaults(
22+
defineProps<{
23+
name: string
24+
title?: string
25+
description?: string
26+
icon?: Component
27+
width?: 'sm' | 'md' | 'lg'
28+
}>(),
29+
{
30+
icon: () => GraduationCap,
31+
width: 'md',
32+
}
33+
)
34+
35+
const computedTitle = computed(
36+
() => props.title || __('No {0} Found').format(props.name)
37+
)
38+
39+
const computedDescription = computed(
40+
() =>
41+
props.description ||
42+
__(
43+
'There are no {0} currently. Keep an eye out, fresh learning experiences are on the way!'
44+
).format(props.name?.toLowerCase())
45+
)
46+
47+
const widthClass = computed(() => {
48+
switch (props.width) {
49+
case 'sm':
50+
return 'w-2/12'
51+
case 'lg':
52+
return 'w-8/12'
53+
default:
54+
return 'w-4/12'
55+
}
56+
})
57+
</script>
File renamed without changes.
File renamed without changes.

frontend/src/components/Settings/Badges.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@
9292
</ListRows>
9393
</ListView>
9494
</div>
95+
<EmptyStateLayout
96+
v-else
97+
name="Badges"
98+
:description="__('Add one to get started.')"
99+
:icon="Award"
100+
/>
95101
</div>
96102
<BadgeForm
97103
v-model="showForm"
@@ -116,10 +122,11 @@ import {
116122
toast,
117123
} from 'frappe-ui'
118124
import { computed, ref } from 'vue'
119-
import { Plus } from 'lucide-vue-next'
125+
import { Award, Plus } from 'lucide-vue-next'
120126
import { cleanError } from '@/utils'
121127
import BadgeForm from '@/components/Settings/BadgeForm.vue'
122128
import BadgeAssignments from '@/components/Settings/BadgeAssignments.vue'
129+
import EmptyStateLayout from '@/components/Layouts/EmptyStateLayout.vue'
123130
124131
const showForm = ref<boolean>(false)
125132
const selectedBadge = ref<string | null>(null)

frontend/src/components/Settings/Categories.vue

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
<div class="overflow-y-auto">
4848
<div class="divide-y divide-outline-gray-modals space-y-2">
4949
<div
50+
v-if="categories.data?.length"
5051
v-for="(cat, index) in categories.data"
5152
:key="cat.name"
5253
class="pt-2"
@@ -78,6 +79,12 @@
7879
@keyup.enter="saveChanges(cat.name, editedValue)"
7980
/>
8081
</div>
82+
<EmptyStateLayout
83+
v-else
84+
name="Categories"
85+
:description="__('Add one to get started.')"
86+
:icon="Network"
87+
/>
8188
</div>
8289
</div>
8390
</div>
@@ -91,9 +98,10 @@ import {
9198
createResource,
9299
toast,
93100
} from 'frappe-ui'
94-
import { Plus, Trash2, X } from 'lucide-vue-next'
101+
import { Plus, Trash2, X, Network } from 'lucide-vue-next'
95102
import { ref } from 'vue'
96103
import { cleanError } from '@/utils'
104+
import EmptyStateLayout from '@/components/Layouts/EmptyStateLayout.vue'
97105
98106
const showForm = ref(false)
99107
const category = ref(null)

frontend/src/components/Settings/Coupons/CouponList.vue

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,12 @@
7878
</ListSelectBanner>
7979
</ListView>
8080
</div>
81-
<div v-else class="text-center text-ink-gray-6 italic mt-40">
82-
{{ __('No coupons created yet.') }}
83-
</div>
81+
<EmptyStateLayout
82+
v-else
83+
name="Coupons"
84+
:description="__('Add one to get started.')"
85+
:icon="Ticket"
86+
/>
8487
</div>
8588
</template>
8689
<script setup lang="ts">
@@ -100,8 +103,9 @@ import {
100103
toast,
101104
} from 'frappe-ui'
102105
import { computed, getCurrentInstance, inject, ref } from 'vue'
103-
import { Plus, Trash2 } from 'lucide-vue-next'
106+
import { Plus, Trash2, Ticket } from 'lucide-vue-next'
104107
import type { Coupon, Coupons } from './types'
108+
import EmptyStateLayout from '@/components/Layouts/EmptyStateLayout.vue'
105109
106110
const dayjs = inject('$dayjs') as typeof import('dayjs')
107111
const app = getCurrentInstance()

frontend/src/components/Settings/EmailTemplates.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@
7070
</ListSelectBanner>
7171
</ListView>
7272
</div>
73+
<EmptyStateLayout
74+
v-else
75+
name="Email Templates"
76+
:description="__('Add one to get started.')"
77+
:icon="MailPlus"
78+
/>
7379
</div>
7480
<EmailTemplateModal
7581
v-model="showForm"
@@ -92,8 +98,9 @@ import {
9298
toast,
9399
} from 'frappe-ui'
94100
import { computed, ref } from 'vue'
95-
import { Plus, Trash2 } from 'lucide-vue-next'
101+
import { Plus, Trash2, MailPlus } from 'lucide-vue-next'
96102
import EmailTemplateModal from '@/components/Modals/EmailTemplateModal.vue'
103+
import EmptyStateLayout from '@/components/Layouts/EmptyStateLayout.vue'
97104
98105
const props = defineProps({
99106
label: {

0 commit comments

Comments
 (0)