Merge pull request #3737 from Budibase/feature/home-screen-redesign
Home Screen Redesign
This commit is contained in:
commit
841feac5fa
|
@ -7,7 +7,7 @@
|
|||
export let disabled = false
|
||||
export let id = null
|
||||
export let updateOnChange = true
|
||||
|
||||
export let quiet = false
|
||||
const dispatch = createEventDispatcher()
|
||||
let focus = false
|
||||
|
||||
|
@ -41,6 +41,7 @@
|
|||
<div class="spectrum-Search" class:is-disabled={disabled}>
|
||||
<div
|
||||
class="spectrum-Textfield"
|
||||
class:spectrum-Textfield--quiet={quiet}
|
||||
class:is-focused={focus}
|
||||
class:is-disabled={disabled}
|
||||
>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
export let placeholder = null
|
||||
export let disabled = false
|
||||
export let updateOnChange = true
|
||||
export let quiet = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const onChange = e => {
|
||||
|
@ -23,6 +24,7 @@
|
|||
{disabled}
|
||||
{value}
|
||||
{placeholder}
|
||||
{quiet}
|
||||
on:change={onChange}
|
||||
on:click
|
||||
on:input
|
||||
|
|
|
@ -10,7 +10,7 @@ it("should rename an unpublished application", () => {
|
|||
cy.get(".home-logo").click()
|
||||
renameApp(appRename)
|
||||
cy.searchForApplication(appRename)
|
||||
cy.get(".appGrid").find(".wrapper").should("have.length", 1)
|
||||
cy.get(".appTable").find(".title").should("have.length", 1)
|
||||
cy.deleteApp(appRename)
|
||||
})
|
||||
|
||||
|
@ -29,7 +29,7 @@ xit("Should rename a published application", () => {
|
|||
cy.get(".home-logo").click()
|
||||
renameApp(appRename, true)
|
||||
cy.searchForApplication(appRename)
|
||||
cy.get(".appGrid").find(".wrapper").should("have.length", 1)
|
||||
cy.get(".appTable").find(".title").should("have.length", 1)
|
||||
})
|
||||
|
||||
it("Should try to rename an application to have no name", () => {
|
||||
|
@ -38,7 +38,7 @@ it("Should try to rename an application to have no name", () => {
|
|||
// Close modal and confirm name has not been changed
|
||||
cy.get(".spectrum-Dialog-grid").contains("Cancel").click()
|
||||
cy.searchForApplication("Cypress Tests")
|
||||
cy.get(".appGrid").find(".wrapper").should("have.length", 1)
|
||||
cy.get(".appTable").find(".title").should("have.length", 1)
|
||||
})
|
||||
|
||||
xit("Should create two applications with the same name", () => {
|
||||
|
@ -64,7 +64,7 @@ it("should validate application names", () => {
|
|||
cy.get(".home-logo").click()
|
||||
renameApp(numberName)
|
||||
cy.searchForApplication(numberName)
|
||||
cy.get(".appGrid").find(".wrapper").should("have.length", 1)
|
||||
cy.get(".appTable").find(".title").should("have.length", 1)
|
||||
renameApp(specialCharName)
|
||||
cy.get(".error").should("have.text", "App name must be letters, numbers and spaces only")
|
||||
})
|
||||
|
@ -74,14 +74,14 @@ it("should validate application names", () => {
|
|||
.its("body")
|
||||
.then(val => {
|
||||
if (val.length > 0) {
|
||||
cy.get(".title > :nth-child(3) > .spectrum-Icon").click()
|
||||
cy.get(".appTable > :nth-child(5) > :nth-child(2) > .spectrum-Icon").click()
|
||||
// Check for when an app is published
|
||||
if (published == true){
|
||||
// Should not have Edit as option, will unpublish app
|
||||
cy.should("not.have.value", "Edit")
|
||||
cy.get(".spectrum-Menu").contains("Unpublish").click()
|
||||
cy.get(".spectrum-Dialog-grid").contains("Unpublish app").click()
|
||||
cy.get(".title > :nth-child(3) > .spectrum-Icon").click()
|
||||
cy.get(".appTable > :nth-child(5) > :nth-child(2) > .spectrum-Icon").click()
|
||||
}
|
||||
cy.contains("Edit").click()
|
||||
cy.get(".spectrum-Modal")
|
||||
|
|
|
@ -50,7 +50,9 @@ Cypress.Commands.add("deleteApp", appName => {
|
|||
.its("body")
|
||||
.then(val => {
|
||||
if (val.length > 0) {
|
||||
cy.get(".title > :nth-child(3) > .spectrum-Icon").click()
|
||||
cy.get(
|
||||
".appTable > :nth-child(5) > :nth-child(2) > .spectrum-Icon"
|
||||
).click()
|
||||
cy.contains("Delete").click()
|
||||
cy.get(".spectrum-Modal").within(() => {
|
||||
cy.get("input").type(appName)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<script>
|
||||
import { gradient } from "actions"
|
||||
import {
|
||||
Heading,
|
||||
Button,
|
||||
|
@ -18,14 +17,19 @@
|
|||
export let deleteApp
|
||||
export let unpublishApp
|
||||
export let releaseLock
|
||||
export let editIcon
|
||||
</script>
|
||||
|
||||
<div class="title">
|
||||
<div class="preview" use:gradient={{ seed: app.name }} />
|
||||
<div class="name" on:click={() => editApp(app)}>
|
||||
<Heading size="XS">
|
||||
{app.name}
|
||||
</Heading>
|
||||
<div style="display: flex;">
|
||||
<div style="color: {app.icon?.color || ''}">
|
||||
<Icon size="XL" name={app.icon?.name || "Apps"} />
|
||||
</div>
|
||||
<div class="name" on:click={() => editApp(app)}>
|
||||
<Heading size="XS">
|
||||
{app.name}
|
||||
</Heading>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="desktop">
|
||||
|
@ -62,6 +66,7 @@
|
|||
disabled={app.lockedOther}
|
||||
on:click={() => editApp(app)}
|
||||
size="S"
|
||||
quiet
|
||||
secondary>Open</Button
|
||||
>
|
||||
<ActionMenu align="right">
|
||||
|
@ -86,15 +91,11 @@
|
|||
<MenuItem on:click={() => updateApp(app)} icon="Edit">Edit</MenuItem>
|
||||
<MenuItem on:click={() => deleteApp(app)} icon="Delete">Delete</MenuItem>
|
||||
{/if}
|
||||
<MenuItem on:click={() => editIcon(app)} icon="Brush">Edit Icon</MenuItem>
|
||||
</ActionMenu>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.preview {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: var(--border-radius-s);
|
||||
}
|
||||
.name {
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
|
@ -103,6 +104,7 @@
|
|||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
margin-left: calc(1.5 * var(--spacing-xl));
|
||||
}
|
||||
.title :global(h1:hover) {
|
||||
color: var(--spectrum-global-color-blue-600);
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
<script>
|
||||
import { ModalContent, Modal, Icon, ColorPicker, Label } from "@budibase/bbui"
|
||||
import { apps } from "stores/portal"
|
||||
|
||||
export let app
|
||||
let modal
|
||||
$: selectedIcon = app?.icon?.name
|
||||
$: selectedColor = app?.icon?.color
|
||||
|
||||
let iconsList = [
|
||||
"Actions",
|
||||
"ConversionFunnel",
|
||||
"App",
|
||||
"Briefcase",
|
||||
"Money",
|
||||
"ShoppingCart",
|
||||
"Form",
|
||||
"Help",
|
||||
"Monitoring",
|
||||
"Sandbox",
|
||||
"Project",
|
||||
"Organisations",
|
||||
"Magnify",
|
||||
"Launch",
|
||||
"Car",
|
||||
"Camera",
|
||||
"Bug",
|
||||
"Channel",
|
||||
"Calculator",
|
||||
"Calendar",
|
||||
"GraphDonut",
|
||||
"GraphBarHorizontal",
|
||||
"Demographic",
|
||||
"Apps",
|
||||
]
|
||||
export const show = () => {
|
||||
modal.show()
|
||||
}
|
||||
export const hide = () => {
|
||||
modal.hide()
|
||||
}
|
||||
|
||||
const onCancel = () => {
|
||||
selectedIcon = ""
|
||||
selectedColor = ""
|
||||
hide()
|
||||
}
|
||||
|
||||
const changeColor = val => {
|
||||
selectedColor = val
|
||||
}
|
||||
|
||||
const save = async () => {
|
||||
await apps.update(app.instance._id, {
|
||||
icon: {
|
||||
name: selectedIcon,
|
||||
color: selectedColor,
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal bind:this={modal} on:hide={onCancel}>
|
||||
<ModalContent
|
||||
title={"Edit Icon"}
|
||||
confirmText={"Save"}
|
||||
onConfirm={() => save()}
|
||||
>
|
||||
<div class="scrollable-icons">
|
||||
<div class="title-spacing">
|
||||
<Label>Select an Icon</Label>
|
||||
</div>
|
||||
<div class="grid">
|
||||
{#each iconsList as item}
|
||||
<div
|
||||
class="icon-item"
|
||||
style="color: {item === selectedIcon ? selectedColor : ''}"
|
||||
on:click={() => (selectedIcon = item)}
|
||||
>
|
||||
<Icon name={item} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="color-selection">
|
||||
<div>
|
||||
<Label>Select a Color</Label>
|
||||
</div>
|
||||
<div class="color-selection-item">
|
||||
<ColorPicker
|
||||
bind:value={selectedColor}
|
||||
on:change={e => changeColor(e.detail)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
<style>
|
||||
.scrollable-icons {
|
||||
overflow-y: auto;
|
||||
height: 230px;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-gap: 20px;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
|
||||
}
|
||||
|
||||
.color-selection {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.color-selection-item {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.title-spacing {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.icon-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
|
@ -1,13 +1,7 @@
|
|||
<script>
|
||||
import { writable, get as svelteGet } from "svelte/store"
|
||||
import {
|
||||
notifications,
|
||||
Input,
|
||||
ModalContent,
|
||||
Dropzone,
|
||||
Body,
|
||||
Checkbox,
|
||||
} from "@budibase/bbui"
|
||||
|
||||
import { notifications, Input, ModalContent, Dropzone } from "@budibase/bbui"
|
||||
import { store, automationStore, hostingStore } from "builderStore"
|
||||
import { admin, auth } from "stores/portal"
|
||||
import { string, mixed, object } from "yup"
|
||||
|
@ -147,16 +141,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
function getModalTitle() {
|
||||
let title = "Create App"
|
||||
if (template.fromFile) {
|
||||
title = "Import App"
|
||||
} else if (template.key) {
|
||||
title = "Create app from template"
|
||||
}
|
||||
return title
|
||||
}
|
||||
|
||||
async function onCancel() {
|
||||
template = null
|
||||
await auth.setInitInfo({})
|
||||
|
@ -187,7 +171,7 @@
|
|||
</ModalContent>
|
||||
{:else}
|
||||
<ModalContent
|
||||
title={getModalTitle()}
|
||||
title={"Name your app"}
|
||||
confirmText={template?.fromFile ? "Import app" : "Create app"}
|
||||
onConfirm={createNewApp}
|
||||
onCancel={inline ? onCancel : null}
|
||||
|
@ -207,16 +191,14 @@
|
|||
}}
|
||||
/>
|
||||
{/if}
|
||||
<Body size="S">
|
||||
Give your new app a name, and choose which groups have access (paid plans
|
||||
only).
|
||||
</Body>
|
||||
<Input
|
||||
bind:value={$values.name}
|
||||
error={$touched.name && $errors.name}
|
||||
on:blur={() => ($touched.name = true)}
|
||||
label="Name"
|
||||
placeholder={$auth.user.firstName
|
||||
? `${$auth.user.firstName}'s app`
|
||||
: "My app"}
|
||||
/>
|
||||
<Checkbox label="Group access" disabled value={true} text="All users" />
|
||||
</ModalContent>
|
||||
{/if}
|
||||
|
|
|
@ -1,46 +1,10 @@
|
|||
<script>
|
||||
import { Heading, Layout, Icon, Body } from "@budibase/bbui"
|
||||
import Spinner from "components/common/Spinner.svelte"
|
||||
import api from "builderStore/api"
|
||||
import { Heading, Layout, Icon } from "@budibase/bbui"
|
||||
|
||||
export let onSelect
|
||||
|
||||
async function fetchTemplates() {
|
||||
const response = await api.get("/api/templates?type=app")
|
||||
return await response.json()
|
||||
}
|
||||
|
||||
let templatesPromise = fetchTemplates()
|
||||
</script>
|
||||
|
||||
<Layout gap="XS" noPadding>
|
||||
{#await templatesPromise}
|
||||
<div class="spinner-container">
|
||||
<Spinner size="30" />
|
||||
</div>
|
||||
{:then templates}
|
||||
{#if templates?.length > 0}
|
||||
<Body size="M">Select a template below, or start from scratch.</Body>
|
||||
{:else}
|
||||
<Body size="M">Start your app from scratch below.</Body>
|
||||
{/if}
|
||||
<div class="templates">
|
||||
{#each templates as template}
|
||||
<div class="template" on:click={() => onSelect(template)}>
|
||||
<div
|
||||
class="background-icon"
|
||||
style={`background: ${template.background};`}
|
||||
>
|
||||
<Icon name={template.icon} />
|
||||
</div>
|
||||
<Heading size="XS">{template.name}</Heading>
|
||||
<p class="detail">{template?.category?.toUpperCase()}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:catch err}
|
||||
<h1 style="color:red">{err}</h1>
|
||||
{/await}
|
||||
<div class="template start-from-scratch" on:click={() => onSelect(null)}>
|
||||
<div
|
||||
class="background-icon"
|
||||
|
@ -67,15 +31,6 @@
|
|||
</Layout>
|
||||
|
||||
<style>
|
||||
.templates {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-gap: var(--spacing-m);
|
||||
grid-template-columns: 1fr;
|
||||
justify-content: start;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.background-icon {
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
async function updateApp() {
|
||||
try {
|
||||
// Update App
|
||||
await apps.update(app.instance._id, $values.name.trim())
|
||||
await apps.update(app.instance._id, { name: $values.name.trim() })
|
||||
hide()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
|
|
@ -2,33 +2,33 @@
|
|||
import {
|
||||
Heading,
|
||||
Layout,
|
||||
Detail,
|
||||
Button,
|
||||
ActionButton,
|
||||
ActionGroup,
|
||||
ButtonGroup,
|
||||
Input,
|
||||
Select,
|
||||
Modal,
|
||||
Page,
|
||||
notifications,
|
||||
Body,
|
||||
Search,
|
||||
} from "@budibase/bbui"
|
||||
import Spinner from "components/common/Spinner.svelte"
|
||||
import CreateAppModal from "components/start/CreateAppModal.svelte"
|
||||
import UpdateAppModal from "components/start/UpdateAppModal.svelte"
|
||||
import ChooseIconModal from "components/start/ChooseIconModal.svelte"
|
||||
|
||||
import { store, automationStore } from "builderStore"
|
||||
import api, { del, post, get } from "builderStore/api"
|
||||
import { onMount } from "svelte"
|
||||
import { apps, auth, admin } from "stores/portal"
|
||||
import { apps, auth, admin, templates } from "stores/portal"
|
||||
import download from "downloadjs"
|
||||
import { goto } from "@roxi/routify"
|
||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||
import AppCard from "components/start/AppCard.svelte"
|
||||
import AppRow from "components/start/AppRow.svelte"
|
||||
import { AppStatus } from "constants"
|
||||
import analytics, { Events } from "analytics"
|
||||
|
||||
let layout = "grid"
|
||||
let sortBy = "name"
|
||||
let template
|
||||
let selectedApp
|
||||
|
@ -36,13 +36,13 @@
|
|||
let updatingModal
|
||||
let deletionModal
|
||||
let unpublishModal
|
||||
let iconModal
|
||||
let creatingApp = false
|
||||
let loaded = false
|
||||
let searchTerm = ""
|
||||
let cloud = $admin.cloud
|
||||
let appName = ""
|
||||
let creatingFromTemplate = false
|
||||
|
||||
$: enrichedApps = enrichApps($apps, $auth.user, sortBy)
|
||||
$: filteredApps = enrichedApps.filter(app =>
|
||||
app?.name?.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
|
@ -172,6 +172,11 @@
|
|||
$goto(`../../app/${app.devId}`)
|
||||
}
|
||||
|
||||
const editIcon = app => {
|
||||
selectedApp = app
|
||||
iconModal.show()
|
||||
}
|
||||
|
||||
const exportApp = app => {
|
||||
const id = app.deployed ? app.prodId : app.devId
|
||||
const appName = encodeURIComponent(app.name)
|
||||
|
@ -262,6 +267,7 @@
|
|||
|
||||
onMount(async () => {
|
||||
await apps.load()
|
||||
await templates.load()
|
||||
// if the portal is loaded from an external URL with a template param
|
||||
const initInfo = await auth.getInitInfo()
|
||||
if (initInfo?.init_template) {
|
||||
|
@ -274,21 +280,66 @@
|
|||
</script>
|
||||
|
||||
<Page wide>
|
||||
{#if loaded && enrichedApps.length}
|
||||
<Layout noPadding>
|
||||
<Layout noPadding>
|
||||
<div class="title">
|
||||
<Heading size="S">Welcome to Budibase</Heading>
|
||||
|
||||
<ButtonGroup>
|
||||
{#if cloud}
|
||||
<Button secondary on:click={initiateAppsExport}>Export apps</Button>
|
||||
{/if}
|
||||
<Button icon="Import" quiet secondary on:click={initiateAppImport}
|
||||
>Import app</Button
|
||||
>
|
||||
<Button icon="Add" cta on:click={initiateAppCreation}>Create app</Button
|
||||
>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
|
||||
<div class="title-text">
|
||||
<Body size="S">Manage your apps and get a head start with templates</Body>
|
||||
</div>
|
||||
<Detail>Quick Start Templates</Detail>
|
||||
<div class="grid">
|
||||
{#each $templates as item}
|
||||
<div
|
||||
on:click={() => {
|
||||
template = item
|
||||
creationModal.show()
|
||||
creatingApp = true
|
||||
}}
|
||||
class="template-card"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div style="color: {item.background}" class="iconAlign">
|
||||
<svg
|
||||
width="26px"
|
||||
height="26px"
|
||||
class="spectrum-Icon"
|
||||
style="color:{item.background};"
|
||||
focusable="false"
|
||||
>
|
||||
<use xlink:href="#spectrum-icon-18-{item.icon}" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="iconAlign">
|
||||
<Body weight="900" size="S">{item.name}</Body>
|
||||
<div style="font-size: 10px;">
|
||||
<Body size="S">{item.category.toUpperCase()}</Body>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{#if loaded && enrichedApps.length}
|
||||
<div class="title">
|
||||
<Heading>Apps</Heading>
|
||||
<ButtonGroup>
|
||||
{#if cloud}
|
||||
<Button secondary on:click={initiateAppsExport}>Export apps</Button>
|
||||
{/if}
|
||||
<Button secondary on:click={initiateAppImport}>Import app</Button>
|
||||
<Button cta on:click={initiateAppCreation}>Create app</Button>
|
||||
</ButtonGroup>
|
||||
<Detail>My Apps</Detail>
|
||||
</div>
|
||||
<div class="filter">
|
||||
<div class="select">
|
||||
<Select
|
||||
quiet
|
||||
autoWidth
|
||||
bind:value={sortBy}
|
||||
placeholder={null}
|
||||
|
@ -299,35 +350,18 @@
|
|||
]}
|
||||
/>
|
||||
<div class="desktop-search">
|
||||
<Search placeholder="Search" bind:value={searchTerm} />
|
||||
<Search quiet placeholder="Search" bind:value={searchTerm} />
|
||||
</div>
|
||||
</div>
|
||||
<ActionGroup>
|
||||
<ActionButton
|
||||
on:click={() => (layout = "grid")}
|
||||
selected={layout === "grid"}
|
||||
quiet
|
||||
icon="ClassicGridView"
|
||||
/>
|
||||
<ActionButton
|
||||
on:click={() => (layout = "table")}
|
||||
selected={layout === "table"}
|
||||
quiet
|
||||
icon="ViewRow"
|
||||
/>
|
||||
</ActionGroup>
|
||||
</div>
|
||||
<div class="mobile-search">
|
||||
<Search placeholder="Search" bind:value={searchTerm} />
|
||||
</div>
|
||||
<div
|
||||
class:appGrid={layout === "grid"}
|
||||
class:appTable={layout === "table"}
|
||||
>
|
||||
<div class="appTable">
|
||||
{#each filteredApps as app (app.appId)}
|
||||
<svelte:component
|
||||
this={layout === "grid" ? AppCard : AppRow}
|
||||
<AppRow
|
||||
{releaseLock}
|
||||
{editIcon}
|
||||
{app}
|
||||
{unpublishApp}
|
||||
{viewApp}
|
||||
|
@ -338,22 +372,23 @@
|
|||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</Layout>
|
||||
{/if}
|
||||
{#if !enrichedApps.length && !creatingApp && loaded}
|
||||
<div class="empty-wrapper">
|
||||
<Modal inline>
|
||||
<CreateAppModal {template} inline={true} />
|
||||
</Modal>
|
||||
</div>
|
||||
{/if}
|
||||
{#if creatingFromTemplate}
|
||||
<div class="empty-wrapper">
|
||||
<p>Creating your Budibase app from your selected template...</p>
|
||||
<Spinner size="10" />
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if !enrichedApps.length && !creatingApp && loaded}
|
||||
<div class="empty-wrapper">
|
||||
<Modal inline>
|
||||
<CreateAppModal {template} inline={true} />
|
||||
</Modal>
|
||||
</div>
|
||||
{/if}
|
||||
{#if creatingFromTemplate}
|
||||
<div class="empty-wrapper">
|
||||
<p>Creating your Budibase app from your selected template...</p>
|
||||
<Spinner size="10" />
|
||||
</div>
|
||||
{/if}
|
||||
</Layout>
|
||||
</Page>
|
||||
|
||||
<Modal
|
||||
bind:this={creationModal}
|
||||
padding={false}
|
||||
|
@ -389,6 +424,7 @@
|
|||
</ConfirmDialog>
|
||||
|
||||
<UpdateAppModal app={selectedApp} bind:this={updatingModal} />
|
||||
<ChooseIconModal app={selectedApp} bind:this={iconModal} />
|
||||
|
||||
<style>
|
||||
.title,
|
||||
|
@ -397,7 +433,7 @@
|
|||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 560px) {
|
||||
|
@ -405,12 +441,48 @@
|
|||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.iconAlign {
|
||||
padding: 0 0 0 var(--spacing-m);
|
||||
display: inline-block;
|
||||
}
|
||||
.template-card {
|
||||
height: 80px;
|
||||
width: 270px;
|
||||
border-radius: var(--border-radius-s);
|
||||
margin-bottom: var(--spacing-m);
|
||||
border: 1px solid var(--spectrum-global-color-gray-300);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
margin-top: calc(var(--spacing-xl) * -1);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-gap: 5px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
|
||||
}
|
||||
|
||||
@media (min-width: 200px) {
|
||||
}
|
||||
|
||||
.select {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 10px;
|
||||
grid-template-columns: auto auto;
|
||||
grid-gap: 30px;
|
||||
}
|
||||
.filter :global(.spectrum-ActionGroup) {
|
||||
flex-wrap: nowrap;
|
||||
|
@ -419,11 +491,6 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
.appGrid {
|
||||
display: grid;
|
||||
grid-gap: 50px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
}
|
||||
.appTable {
|
||||
display: grid;
|
||||
grid-template-rows: auto;
|
||||
|
@ -464,4 +531,8 @@
|
|||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.template-card:hover {
|
||||
background: var(--spectrum-alias-background-color-tertiary);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -65,16 +65,17 @@ export function createAppStore() {
|
|||
}
|
||||
}
|
||||
|
||||
async function update(appId, name) {
|
||||
const response = await api.put(`/api/applications/${appId}`, { name })
|
||||
async function update(appId, value) {
|
||||
console.log({ value })
|
||||
const response = await api.put(`/api/applications/${appId}`, { ...value })
|
||||
if (response.status === 200) {
|
||||
store.update(state => {
|
||||
const updatedAppIndex = state.findIndex(
|
||||
app => app.instance._id === appId
|
||||
)
|
||||
if (updatedAppIndex !== -1) {
|
||||
const updatedApp = state[updatedAppIndex]
|
||||
updatedApp.name = name
|
||||
let updatedApp = state[updatedAppIndex]
|
||||
updatedApp = { ...updatedApp, ...value }
|
||||
state.apps = state.splice(updatedAppIndex, 1, updatedApp)
|
||||
}
|
||||
return state
|
||||
|
|
|
@ -5,3 +5,4 @@ export { apps } from "./apps"
|
|||
export { email } from "./email"
|
||||
export { auth } from "./auth"
|
||||
export { oidc } from "./oidc"
|
||||
export { templates } from "./templates"
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import { writable } from "svelte/store"
|
||||
import api from "builderStore/api"
|
||||
|
||||
export function templatesStore() {
|
||||
const { subscribe, set } = writable([])
|
||||
|
||||
async function load() {
|
||||
const response = await api.get("/api/templates?type=app")
|
||||
const json = await response.json()
|
||||
set(json)
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
load,
|
||||
}
|
||||
}
|
||||
|
||||
export const templates = templatesStore()
|
Loading…
Reference in New Issue