Merge pull request #11686 from Budibase/cheeks-lab-day-portal-poc
Use apps within the portal
This commit is contained in:
commit
0578f96839
|
@ -101,7 +101,7 @@
|
|||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
border-bottom: var(--border-light);
|
||||
padding: 0 24px;
|
||||
padding: 0 var(--spacing-l);
|
||||
gap: 24px;
|
||||
position: relative;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<script>
|
||||
import { params, redirect } from "@roxi/routify"
|
||||
import { apps } from "stores/portal"
|
||||
|
||||
$: app = $apps.find(app => app.appId === $params.appId)
|
||||
$: {
|
||||
if (!app) {
|
||||
$redirect("../")
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if app}
|
||||
<slot />
|
||||
{/if}
|
|
@ -0,0 +1,79 @@
|
|||
<script>
|
||||
import { params, goto } from "@roxi/routify"
|
||||
import { apps, auth, sideBarCollapsed } from "stores/portal"
|
||||
import { ActionButton } from "@budibase/bbui"
|
||||
import { sdk } from "@budibase/shared-core"
|
||||
|
||||
$: app = $apps.find(app => app.appId === $params.appId)
|
||||
$: iframeUrl = getIframeURL(app)
|
||||
$: isBuilder = sdk.users.isBuilder($auth.user, app?.devId)
|
||||
|
||||
const getIframeURL = app => {
|
||||
if (app.status === "published") {
|
||||
return `/app${app.url}`
|
||||
}
|
||||
return `/${app.devId}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
{#if $sideBarCollapsed}
|
||||
<ActionButton
|
||||
quiet
|
||||
icon="Rail"
|
||||
on:click={() => sideBarCollapsed.set(false)}
|
||||
>
|
||||
Menu
|
||||
</ActionButton>
|
||||
{:else}
|
||||
<ActionButton
|
||||
quiet
|
||||
icon="RailRightOpen"
|
||||
on:click={() => sideBarCollapsed.set(true)}
|
||||
>
|
||||
Collapse
|
||||
</ActionButton>
|
||||
{/if}
|
||||
{#if isBuilder}
|
||||
<ActionButton
|
||||
quiet
|
||||
icon="Edit"
|
||||
on:click={() => $goto(`../../app/${app.devId}`)}
|
||||
>
|
||||
Edit
|
||||
</ActionButton>
|
||||
{/if}
|
||||
<ActionButton
|
||||
quiet
|
||||
icon="LinkOut"
|
||||
on:click={() => window.open(iframeUrl, "_blank")}
|
||||
>
|
||||
Fullscreen
|
||||
</ActionButton>
|
||||
</div>
|
||||
<iframe src={iframeUrl} title={app.name} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
padding: 0 var(--spacing-l) var(--spacing-l) var(--spacing-l);
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: var(--spacing-xs);
|
||||
flex: 0 0 50px;
|
||||
}
|
||||
iframe {
|
||||
flex: 1 1 auto;
|
||||
border-radius: var(--spacing-s);
|
||||
border: 1px solid var(--spectrum-global-color-gray-300);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,140 @@
|
|||
<script>
|
||||
import { Icon, Body } from "@budibase/bbui"
|
||||
import { apps, sideBarCollapsed } from "stores/portal"
|
||||
import { params, goto } from "@roxi/routify"
|
||||
import { tick } from "svelte"
|
||||
import NavItem from "components/common/NavItem.svelte"
|
||||
|
||||
let searchInput
|
||||
let searchString
|
||||
let searching = false
|
||||
|
||||
$: filteredApps = $apps.filter(app => {
|
||||
return (
|
||||
!searchString ||
|
||||
app.name.toLowerCase().includes(searchString.toLowerCase())
|
||||
)
|
||||
})
|
||||
|
||||
const startSearching = async () => {
|
||||
searching = true
|
||||
searchString = ""
|
||||
await tick()
|
||||
searchInput.focus()
|
||||
}
|
||||
|
||||
const stopSearching = () => {
|
||||
searching = false
|
||||
searchString = ""
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="side-bar" class:collapsed={$sideBarCollapsed}>
|
||||
<div class="side-bar-controls">
|
||||
{#if searching}
|
||||
<input
|
||||
bind:this={searchInput}
|
||||
bind:value={searchString}
|
||||
placeholder="Search for apps"
|
||||
/>
|
||||
{:else}
|
||||
<Body size="S">Apps</Body>
|
||||
<Icon name="Search" size="S" hoverable on:click={startSearching} />
|
||||
{/if}
|
||||
<div class="rotational" class:rotated={searching}>
|
||||
<Icon
|
||||
name="Add"
|
||||
hoverable
|
||||
on:click={searching ? stopSearching : () => $goto("./create")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="side-bar-nav">
|
||||
<NavItem
|
||||
icon="WebPages"
|
||||
text="All apps"
|
||||
on:click={() => $goto("./")}
|
||||
selected={!$params.appId}
|
||||
/>
|
||||
{#each filteredApps as app}
|
||||
<NavItem
|
||||
text={app.name}
|
||||
icon={app.icon?.name || "Apps"}
|
||||
iconColor={app.icon?.color}
|
||||
selected={$params.appId === app.appId}
|
||||
on:click={() => $goto(`./${app.appId}`)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.side-bar {
|
||||
flex: 0 0 260px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
border-right: var(--border-light);
|
||||
background: var(--spectrum-global-color-gray-100);
|
||||
overflow: hidden;
|
||||
transition: margin-left 300ms ease-out;
|
||||
}
|
||||
.side-bar.collapsed {
|
||||
margin-left: -262px;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.side-bar {
|
||||
margin-left: -262px;
|
||||
}
|
||||
}
|
||||
|
||||
.side-bar-controls {
|
||||
flex: 0 0 50px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: var(--spacing-l);
|
||||
padding: 0 var(--spacing-l);
|
||||
}
|
||||
.side-bar-controls :global(.spectrum-Body),
|
||||
.side-bar-controls input {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.side-bar-controls :global(.spectrum-Icon) {
|
||||
color: var(--spectrum-global-color-gray-700);
|
||||
}
|
||||
|
||||
input {
|
||||
outline: none;
|
||||
border: none;
|
||||
max-width: none;
|
||||
flex: 1 1 auto;
|
||||
color: var(--spectrum-global-color-gray-800);
|
||||
font-size: 14px;
|
||||
padding: 0;
|
||||
transition: border 130ms ease-out;
|
||||
font-family: var(--font-sans);
|
||||
background: inherit;
|
||||
}
|
||||
input::placeholder {
|
||||
color: var(--spectrum-global-color-gray-700);
|
||||
transition: color 130ms ease-out;
|
||||
}
|
||||
input:hover::placeholder {
|
||||
color: var(--spectrum-global-color-gray-800);
|
||||
}
|
||||
|
||||
.side-bar-nav {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
div.rotational {
|
||||
transition: transform 130ms ease-out;
|
||||
}
|
||||
div.rotational.rotated {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
</style>
|
|
@ -11,6 +11,7 @@
|
|||
import { onMount } from "svelte"
|
||||
import { redirect } from "@roxi/routify"
|
||||
import { sdk } from "@budibase/shared-core"
|
||||
import PortalSideBar from "./_components/PortalSideBar.svelte"
|
||||
|
||||
// Don't block loading if we've already hydrated state
|
||||
let loaded = $apps.length != null
|
||||
|
@ -44,5 +45,18 @@
|
|||
</script>
|
||||
|
||||
{#if loaded}
|
||||
<slot />
|
||||
<div class="page">
|
||||
<PortalSideBar />
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.page {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { writable } from "svelte/store"
|
||||
|
||||
export { organisation } from "./organisation"
|
||||
export { users } from "./users"
|
||||
export { admin } from "./admin"
|
||||
|
@ -14,3 +16,5 @@ export { environment } from "./environment"
|
|||
export { menu } from "./menu"
|
||||
export { auditLogs } from "./auditLogs"
|
||||
export { features } from "./features"
|
||||
|
||||
export const sideBarCollapsed = writable(false)
|
||||
|
|
Loading…
Reference in New Issue