Skip to content

Commit 346849b

Browse files
authored
List all application types in left sidebar (baserow#4589)
1 parent cac1284 commit 346849b

3 files changed

Lines changed: 114 additions & 55 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"type": "feature",
3+
"message": "List all application types in left sidebar, even if there are no items in there.",
4+
"domain": "core",
5+
"issue_number": null,
6+
"bullet_points": [],
7+
"created_at": "2026-01-26"
8+
}

web-frontend/modules/core/assets/scss/components/tree.scss

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
margin: 0;
66

77
&:not(:last-child) {
8-
margin-bottom: 12px;
8+
margin-bottom: 8px;
99
}
1010

1111
.tree__item & {
@@ -312,8 +312,40 @@
312312
}
313313

314314
.tree__heading {
315+
display: flex;
316+
margin: 8px;
317+
justify-content: space-between;
318+
align-items: center;
319+
}
320+
321+
.tree__heading-name {
315322
font-size: 11px;
316323
font-weight: 500;
317324
color: $palette-neutral-900;
318-
margin: 8px;
325+
line-height: 16px;
326+
}
327+
328+
.tree__heading-add {
329+
color: $palette-neutral-600;
330+
width: 16px;
331+
height: 16px;
332+
display: flex;
333+
align-items: center;
334+
justify-content: center;
335+
font-size: 16px;
336+
line-height: 16px;
337+
338+
@include rounded($rounded);
339+
340+
&:hover {
341+
text-decoration: none;
342+
background-color: $palette-neutral-200;
343+
outline: solid 2px $palette-neutral-200;
344+
color: $palette-neutral-800;
345+
}
346+
}
347+
348+
.tree__separator {
349+
border-bottom: solid 1px $palette-neutral-200;
350+
margin: 8px 8px 16px;
319351
}

web-frontend/modules/core/components/sidebar/SidebarWithWorkspace.vue

Lines changed: 72 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22
<div class="sidebar__section sidebar__section--scrollable">
3-
<div v-if="hasItems" class="sidebar__section-scrollable">
3+
<div class="sidebar__section-scrollable">
44
<div
55
class="sidebar__section-scrollable-inner"
66
data-highlight="applications"
@@ -14,52 +14,66 @@
1414
>
1515
</component>
1616
</ul>
17-
<ul v-if="applicationsCount" class="tree">
17+
<ul class="tree">
1818
<li
19-
v-for="applicationGroup in groupedApplicationsForSelectedWorkspace"
19+
v-for="(applicationGroup, index) in groupedApplicationsForSelectedWorkspace"
2020
:key="applicationGroup.type"
2121
>
22-
<template v-if="applicationGroup.applications.length > 0">
23-
<div class="tree__heading">
22+
<div class="tree__heading" :class="{'margin-bottom-2': applicationGroup.applications.length === 0 && index < groupedApplicationsForSelectedWorkspace.length - 1}">
23+
<div class="tree__heading-name">
2424
{{ applicationGroup.name }}
2525
</div>
26-
<ul
27-
class="tree"
28-
:class="{
29-
'margin-bottom-0': pendingJobs[applicationGroup.type].length,
30-
}"
31-
data-highlight="applications"
26+
<a
27+
v-if="canCreateApplication"
28+
:ref="'createApplicationModalToggle' + applicationGroup.type"
29+
class="tree__heading-add"
30+
@click="openCreateApplicationModal(applicationGroup.type)"
3231
>
33-
<component
34-
:is="getApplicationComponent(application)"
35-
v-for="application in applicationGroup.applications"
36-
:key="application.id"
37-
v-sortable="{
38-
id: application.id,
39-
update: orderApplications,
40-
handle: '[data-sortable-handle]',
41-
marginTop: -1.5,
42-
enabled: $hasPermission(
43-
'workspace.order_applications',
44-
selectedWorkspace,
45-
selectedWorkspace.id
46-
),
47-
}"
48-
:application="application"
49-
:pending-jobs="pendingJobs[application.type]"
32+
<i class="iconoir-plus"></i>
33+
<CreateApplicationModal
34+
:ref="'createApplicationModal' + applicationGroup.type"
35+
:application-type="applicationGroup.applicationType"
5036
:workspace="selectedWorkspace"
51-
></component>
52-
</ul>
53-
<ul v-if="pendingJobs[applicationGroup.type].length" class="tree">
54-
<component
55-
:is="getPendingJobComponent(job)"
56-
v-for="job in pendingJobs[applicationGroup.type]"
57-
:key="job.id"
58-
:job="job"
59-
>
60-
</component>
61-
</ul>
62-
</template>
37+
></CreateApplicationModal>
38+
</a>
39+
</div>
40+
<ul
41+
class="tree"
42+
:class="{
43+
'margin-bottom-0': pendingJobs[applicationGroup.type].length,
44+
}"
45+
data-highlight="applications"
46+
>
47+
<component
48+
:is="getApplicationComponent(application)"
49+
v-for="application in applicationGroup.applications"
50+
:key="application.id"
51+
v-sortable="{
52+
id: application.id,
53+
update: orderApplications,
54+
handle: '[data-sortable-handle]',
55+
marginTop: -1.5,
56+
enabled: $hasPermission(
57+
'workspace.order_applications',
58+
selectedWorkspace,
59+
selectedWorkspace.id
60+
),
61+
}"
62+
:application="application"
63+
:pending-jobs="pendingJobs[application.type]"
64+
:workspace="selectedWorkspace"
65+
></component>
66+
</ul>
67+
<ul v-if="pendingJobs[applicationGroup.type].length" class="tree">
68+
<component
69+
:is="getPendingJobComponent(job)"
70+
v-for="job in pendingJobs[applicationGroup.type]"
71+
:key="job.id"
72+
:job="job"
73+
>
74+
</component>
75+
</ul>
76+
<div v-if="index < groupedApplicationsForSelectedWorkspace.length - 1" class="tree__separator"></div>
6377
</li>
6478
</ul>
6579
</div>
@@ -72,10 +86,7 @@
7286
selectedWorkspace.id
7387
)
7488
"
75-
class="sidebar__new-wrapper"
76-
:class="{
77-
'sidebar__new-wrapper--separator': hasItems,
78-
}"
89+
class="sidebar__new-wrapper sidebar__new-wrapper--separator"
7990
data-highlight="create-new"
8091
>
8192
<a
@@ -100,11 +111,12 @@
100111

101112
<script>
102113
import { notifyIf } from '@baserow/modules/core/utils/error'
114+
import CreateApplicationModal from '@baserow/modules/core/components/application/CreateApplicationModal'
103115
import CreateApplicationContext from '@baserow/modules/core/components/application/CreateApplicationContext'
104116
105117
export default {
106118
name: 'SidebarWithWorkspace',
107-
components: { CreateApplicationContext },
119+
components: { CreateApplicationContext, CreateApplicationModal },
108120
props: {
109121
applications: {
110122
type: Array,
@@ -127,6 +139,7 @@ export default {
127139
return {
128140
name: applicationType.getNamePlural(),
129141
type: applicationType.getType(),
142+
applicationType: applicationType,
130143
developmentStage: applicationType.developmentStage,
131144
applications: this.applications
132145
.filter((application) => {
@@ -141,12 +154,6 @@ export default {
141154
})
142155
return applicationTypes
143156
},
144-
applicationsCount() {
145-
return this.groupedApplicationsForSelectedWorkspace.reduce(
146-
(acc, group) => acc + group.applications.length,
147-
0
148-
)
149-
},
150157
pendingJobs() {
151158
const grouped = { null: [] }
152159
Object.values(this.$registry.getAll('application')).forEach(
@@ -162,8 +169,12 @@ export default {
162169
})
163170
return grouped
164171
},
165-
hasItems() {
166-
return this.applicationsCount || this.pendingJobs.null.length
172+
canCreateApplication() {
173+
return this.$hasPermission(
174+
'workspace.create_application',
175+
this.selectedWorkspace,
176+
this.selectedWorkspace.id
177+
)
167178
},
168179
},
169180
methods: {
@@ -186,6 +197,14 @@ export default {
186197
notifyIf(error, 'application')
187198
}
188199
},
200+
openCreateApplicationModal(type) {
201+
if (!this.canCreateApplication) {
202+
return
203+
}
204+
205+
const target = this.$refs['createApplicationModalToggle' + type]
206+
this.$refs['createApplicationModal' + type][0].toggle(target)
207+
},
189208
},
190209
}
191210
</script>

0 commit comments

Comments
 (0)