forked from tsai/budibase
committed by
GitHub
21 changed files with 1079 additions and 389 deletions
@ -1,133 +1,133 @@ |
|||||
import filterTests from "../support/filterTests" |
import filterTests from "../support/filterTests" |
||||
|
|
||||
filterTests(['all'], () => { |
filterTests(['all'], () => { |
||||
context("Rename an App", () => { |
context("Rename an App", () => { |
||||
beforeEach(() => { |
beforeEach(() => { |
||||
cy.login() |
cy.login() |
||||
cy.createTestApp() |
cy.createTestApp() |
||||
}) |
}) |
||||
|
|
||||
it("should rename an unpublished application", () => { |
it("should rename an unpublished application", () => { |
||||
const appName = "Cypress Tests" |
const appName = "Cypress Tests" |
||||
const appRename = "Cypress Renamed" |
const appRename = "Cypress Renamed" |
||||
// Rename app, Search for app, Confirm name was changed
|
// Rename app, Search for app, Confirm name was changed
|
||||
cy.get(".home-logo").click() |
cy.get(".home-logo").click() |
||||
renameApp(appName, appRename) |
renameApp(appName, appRename) |
||||
cy.reload() |
cy.reload() |
||||
cy.wait(1000) |
cy.wait(1000) |
||||
cy.searchForApplication(appRename) |
cy.get(".appTable").find(".title").should("have.length", 1) |
||||
cy.get(".appTable").find(".title").should("have.length", 1) |
cy.applicationInAppTable(appRename) |
||||
// Set app name back to Cypress Tests
|
// Set app name back to Cypress Tests
|
||||
cy.reload() |
cy.reload() |
||||
cy.wait(1000) |
cy.wait(1000) |
||||
renameApp(appRename, appName) |
renameApp(appRename, appName) |
||||
}) |
}) |
||||
|
|
||||
xit("Should rename a published application", () => { |
xit("Should rename a published application", () => { |
||||
// It is not possible to rename a published application
|
// It is not possible to rename a published application
|
||||
const appName = "Cypress Tests" |
const appName = "Cypress Tests" |
||||
const appRename = "Cypress Renamed" |
const appRename = "Cypress Renamed" |
||||
// Publish the app
|
// Publish the app
|
||||
cy.get(".toprightnav") |
cy.get(".toprightnav") |
||||
cy.get(".spectrum-Button").contains("Publish").click({force: true}) |
cy.get(".spectrum-Button").contains("Publish").click({ force: true }) |
||||
cy.get(".spectrum-Dialog-grid") |
cy.get(".spectrum-Dialog-grid") |
||||
.within(() => { |
.within(() => { |
||||
// Click publish again within the modal
|
// Click publish again within the modal
|
||||
cy.get(".spectrum-Button").contains("Publish").click({force: true}) |
cy.get(".spectrum-Button").contains("Publish").click({ force: true }) |
||||
}) |
}) |
||||
// Rename app, Search for app, Confirm name was changed
|
// Rename app, Search for app, Confirm name was changed
|
||||
cy.get(".home-logo").click() |
cy.get(".home-logo").click() |
||||
renameApp(appName, appRename, true) |
renameApp(appName, appRename, true) |
||||
cy.searchForApplication(appRename) |
cy.get(".appTable").find(".wrapper").should("have.length", 1) |
||||
cy.get(".appTable").find(".wrapper").should("have.length", 1) |
cy.applicationInAppTable(appRename) |
||||
}) |
}) |
||||
|
|
||||
it("Should try to rename an application to have no name", () => { |
it("Should try to rename an application to have no name", () => { |
||||
const appName = "Cypress Tests" |
const appName = "Cypress Tests" |
||||
cy.get(".home-logo").click() |
cy.get(".home-logo").click() |
||||
renameApp(appName, " ", false, true) |
renameApp(appName, " ", false, true) |
||||
cy.wait(500) |
cy.wait(500) |
||||
// Close modal and confirm name has not been changed
|
// Close modal and confirm name has not been changed
|
||||
cy.get(".spectrum-Dialog-grid").contains("Cancel").click() |
cy.get(".spectrum-Dialog-grid").contains("Cancel").click() |
||||
cy.reload() |
cy.reload() |
||||
cy.wait(1000) |
cy.wait(1000) |
||||
cy.searchForApplication(appName) |
cy.applicationInAppTable(appName) |
||||
cy.get(".appTable").find(".title").should("have.length", 1) |
cy.get(".appTable").find(".title").should("have.length", 1) |
||||
|
|
||||
}) |
}) |
||||
|
|
||||
xit("Should create two applications with the same name", () => { |
xit("Should create two applications with the same name", () => { |
||||
// It is not possible to have applications with the same name
|
// It is not possible to have applications with the same name
|
||||
const appName = "Cypress Tests" |
const appName = "Cypress Tests" |
||||
cy.visit(`${Cypress.config().baseUrl}/builder`) |
cy.visit(`${Cypress.config().baseUrl}/builder`) |
||||
cy.wait(500) |
cy.wait(500) |
||||
cy.get(".spectrum-Button").contains("Create app").click({force: true}) |
cy.get(".spectrum-Button").contains("Create app").click({ force: true }) |
||||
cy.contains(/Start from scratch/).click() |
cy.contains(/Start from scratch/).click() |
||||
cy.get(".spectrum-Modal") |
cy.get(".spectrum-Modal") |
||||
.within(() => { |
.within(() => { |
||||
cy.get("input").eq(0).type(appName) |
cy.get("input").eq(0).type(appName) |
||||
cy.get(".spectrum-ButtonGroup").contains("Create app").click({force: true}) |
cy.get(".spectrum-ButtonGroup").contains("Create app").click({ force: true }) |
||||
cy.get(".error").should("have.text", "Another app with the same name already exists") |
cy.get(".error").should("have.text", "Another app with the same name already exists") |
||||
}) |
}) |
||||
}) |
}) |
||||
|
|
||||
it("should validate application names", () => { |
it("should validate application names", () => { |
||||
// App name must be letters, numbers and spaces only
|
// App name must be letters, numbers and spaces only
|
||||
// This test checks numbers and special characters specifically
|
// This test checks numbers and special characters specifically
|
||||
const appName = "Cypress Tests" |
const appName = "Cypress Tests" |
||||
const numberName = 12345 |
const numberName = 12345 |
||||
const specialCharName = "£$%^" |
const specialCharName = "£$%^" |
||||
cy.get(".home-logo").click() |
cy.get(".home-logo").click() |
||||
renameApp(appName, numberName) |
renameApp(appName, numberName) |
||||
cy.reload() |
cy.reload() |
||||
cy.wait(1000) |
cy.wait(1000) |
||||
cy.searchForApplication(numberName) |
cy.applicationInAppTable(numberName) |
||||
cy.get(".appTable").find(".title").should("have.length", 1) |
cy.get(".appTable").find(".title").should("have.length", 1) |
||||
cy.reload() |
cy.reload() |
||||
cy.wait(1000) |
cy.wait(1000) |
||||
renameApp(numberName, specialCharName) |
renameApp(numberName, specialCharName) |
||||
cy.get(".error").should("have.text", "App name must be letters, numbers and spaces only") |
cy.get(".error").should("have.text", "App name must be letters, numbers and spaces only") |
||||
// Set app name back to Cypress Tests
|
// Set app name back to Cypress Tests
|
||||
cy.reload() |
cy.reload() |
||||
cy.wait(1000) |
cy.wait(1000) |
||||
renameApp(numberName, appName) |
renameApp(numberName, appName) |
||||
}) |
}) |
||||
|
|
||||
const renameApp = (originalName, changedName, published, noName) => { |
const renameApp = (originalName, changedName, published, noName) => { |
||||
cy.searchForApplication(originalName) |
cy.applicationInAppTable(originalName) |
||||
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`) |
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`) |
||||
.its("body") |
.its("body") |
||||
.then(val => { |
.then(val => { |
||||
if (val.length > 0) { |
if (val.length > 0) { |
||||
cy.get(".appTable") |
cy.get(".appTable") |
||||
.within(() => { |
.within(() => { |
||||
cy.get(".spectrum-Icon").eq(1).click() |
cy.get(".spectrum-Icon").eq(1).click() |
||||
}) |
}) |
||||
// Check for when an app is published
|
// Check for when an app is published
|
||||
if (published == true){ |
if (published == true) { |
||||
// Should not have Edit as option, will unpublish app
|
// Should not have Edit as option, will unpublish app
|
||||
cy.should("not.have.value", "Edit") |
cy.should("not.have.value", "Edit") |
||||
cy.get(".spectrum-Menu").contains("Unpublish").click() |
cy.get(".spectrum-Menu").contains("Unpublish").click() |
||||
cy.get(".spectrum-Dialog-grid").contains("Unpublish app").click() |
cy.get(".spectrum-Dialog-grid").contains("Unpublish app").click() |
||||
cy.get(".appTable > :nth-child(5) > :nth-child(2) > .spectrum-Icon").click() |
cy.get(".appTable > :nth-child(5) > :nth-child(2) > .spectrum-Icon").click() |
||||
|
} |
||||
|
cy.contains("Edit").click() |
||||
|
cy.get(".spectrum-Modal") |
||||
|
.within(() => { |
||||
|
if (noName == true) { |
||||
|
cy.get("input").clear() |
||||
|
cy.get(".spectrum-Dialog-grid").click() |
||||
|
.contains("App name must be letters, numbers and spaces only") |
||||
|
return cy |
||||
} |
} |
||||
cy.contains("Edit").click() |
cy.get("input").clear() |
||||
cy.get(".spectrum-Modal") |
cy.get("input").eq(0).type(changedName).should("have.value", changedName).blur() |
||||
.within(() => { |
cy.get(".spectrum-ButtonGroup").contains("Save").click({ force: true }) |
||||
if (noName == true){ |
cy.wait(500) |
||||
cy.get("input").clear() |
}) |
||||
cy.get(".spectrum-Dialog-grid").click() |
} |
||||
.contains("App name must be letters, numbers and spaces only") |
|
||||
return cy |
|
||||
} |
|
||||
cy.get("input").clear() |
|
||||
cy.get("input").eq(0).type(changedName).should("have.value", changedName).blur() |
|
||||
cy.get(".spectrum-ButtonGroup").contains("Save").click({force: true}) |
|
||||
cy.wait(500) |
|
||||
}) |
|
||||
} |
|
||||
}) |
}) |
||||
|
|
||||
} |
} |
||||
}) |
}) |
||||
}) |
}) |
||||
|
|||||
@ -0,0 +1,125 @@ |
|||||
|
<script> |
||||
|
export let backgroundColour |
||||
|
export let imageSrc |
||||
|
export let name |
||||
|
export let icon |
||||
|
export let overlayEnabled = true |
||||
|
|
||||
|
let imageError = false |
||||
|
let imageLoaded = false |
||||
|
|
||||
|
const imageRenderError = () => { |
||||
|
imageError = true |
||||
|
} |
||||
|
|
||||
|
const imageLoadSuccess = () => { |
||||
|
imageLoaded = true |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div class="template-card" style="background-color:{backgroundColour};"> |
||||
|
<div class="template-thumbnail card-body"> |
||||
|
<img |
||||
|
alt={name} |
||||
|
src={imageSrc} |
||||
|
on:error={imageRenderError} |
||||
|
on:load={imageLoadSuccess} |
||||
|
class={`${imageLoaded ? "loaded" : ""}`} |
||||
|
/> |
||||
|
<div style={`display:${imageError ? "block" : "none"}`}> |
||||
|
<svg |
||||
|
width="26px" |
||||
|
height="26px" |
||||
|
class="spectrum-Icon" |
||||
|
style="color: white" |
||||
|
focusable="false" |
||||
|
> |
||||
|
<use xlink:href="#spectrum-icon-18-{icon}" /> |
||||
|
</svg> |
||||
|
</div> |
||||
|
<div class={overlayEnabled ? "template-thumbnail-action-overlay" : ""}> |
||||
|
<slot /> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="template-thumbnail-text"> |
||||
|
<div>{name}</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.template-thumbnail { |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.template-card:hover .template-thumbnail-action-overlay { |
||||
|
opacity: 1; |
||||
|
} |
||||
|
|
||||
|
.template-thumbnail-action-overlay { |
||||
|
position: absolute; |
||||
|
top: 0px; |
||||
|
left: 0px; |
||||
|
width: 100%; |
||||
|
height: 70%; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
background-color: rgba(0, 0, 0, 0.7); |
||||
|
opacity: 0; |
||||
|
transition: opacity var(--spectrum-global-animation-duration-100) ease; |
||||
|
border-top-right-radius: inherit; |
||||
|
border-top-left-radius: inherit; |
||||
|
} |
||||
|
|
||||
|
.template-thumbnail-text { |
||||
|
position: absolute; |
||||
|
bottom: 0px; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
height: 30%; |
||||
|
width: 100%; |
||||
|
color: var( |
||||
|
--spectrum-heading-xs-text-color, |
||||
|
var(--spectrum-alias-heading-text-color) |
||||
|
); |
||||
|
background-color: var(--spectrum-global-color-gray-50); |
||||
|
} |
||||
|
|
||||
|
.template-thumbnail-text > div { |
||||
|
padding-left: 1rem; |
||||
|
padding-right: 1rem; |
||||
|
} |
||||
|
|
||||
|
.template-card { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
border-radius: var(--border-radius-s); |
||||
|
border: 1px solid var(--spectrum-global-color-gray-300); |
||||
|
overflow: hidden; |
||||
|
min-height: 200px; |
||||
|
} |
||||
|
|
||||
|
.template-card > * { |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.template-card img.loaded { |
||||
|
display: block; |
||||
|
} |
||||
|
|
||||
|
.template-card img { |
||||
|
display: none; |
||||
|
max-width: 100%; |
||||
|
border-radius: var(--border-radius-s) 0px var(--border-radius-s) 0px; |
||||
|
} |
||||
|
|
||||
|
.template-card:hover { |
||||
|
background: var(--spectrum-alias-background-color-tertiary); |
||||
|
} |
||||
|
|
||||
|
.card-body { |
||||
|
padding-left: 1rem; |
||||
|
padding-top: 1rem; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,152 @@ |
|||||
|
<script> |
||||
|
import { |
||||
|
Layout, |
||||
|
Detail, |
||||
|
Heading, |
||||
|
Button, |
||||
|
Modal, |
||||
|
ActionGroup, |
||||
|
ActionButton, |
||||
|
} from "@budibase/bbui" |
||||
|
import TemplateCard from "components/common/TemplateCard.svelte" |
||||
|
import CreateAppModal from "components/start/CreateAppModal.svelte" |
||||
|
|
||||
|
export let templates |
||||
|
|
||||
|
let selectedTemplateCategory |
||||
|
let creationModal |
||||
|
let template |
||||
|
|
||||
|
const groupTemplatesByCategory = (templates, categoryFilter) => { |
||||
|
let grouped = templates.reduce((acc, template) => { |
||||
|
if ( |
||||
|
typeof categoryFilter === "string" && |
||||
|
[categoryFilter].indexOf(template.category) < 0 |
||||
|
) { |
||||
|
return acc |
||||
|
} |
||||
|
|
||||
|
acc[template.category] = !acc[template.category] |
||||
|
? [] |
||||
|
: acc[template.category] |
||||
|
acc[template.category].push(template) |
||||
|
|
||||
|
return acc |
||||
|
}, {}) |
||||
|
return grouped |
||||
|
} |
||||
|
|
||||
|
$: filteredTemplates = groupTemplatesByCategory( |
||||
|
templates, |
||||
|
selectedTemplateCategory |
||||
|
) |
||||
|
|
||||
|
$: filteredTemplateCategories = filteredTemplates |
||||
|
? Object.keys(filteredTemplates).sort() |
||||
|
: [] |
||||
|
|
||||
|
$: templateCategories = templates |
||||
|
? Object.keys(groupTemplatesByCategory(templates)).sort() |
||||
|
: [] |
||||
|
|
||||
|
const stopAppCreation = () => { |
||||
|
template = null |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div class="template-header"> |
||||
|
<Layout noPadding gap="S"> |
||||
|
<Heading size="S">Templates</Heading> |
||||
|
<div class="template-category-filters spectrum-ActionGroup"> |
||||
|
<ActionGroup> |
||||
|
<ActionButton |
||||
|
selected={!selectedTemplateCategory} |
||||
|
on:click={() => { |
||||
|
selectedTemplateCategory = null |
||||
|
}} |
||||
|
> |
||||
|
All |
||||
|
</ActionButton> |
||||
|
{#each templateCategories as templateCategoryKey} |
||||
|
<ActionButton |
||||
|
dataCy={templateCategoryKey} |
||||
|
selected={templateCategoryKey == selectedTemplateCategory} |
||||
|
on:click={() => { |
||||
|
selectedTemplateCategory = templateCategoryKey |
||||
|
}} |
||||
|
> |
||||
|
{templateCategoryKey} |
||||
|
</ActionButton> |
||||
|
{/each} |
||||
|
</ActionGroup> |
||||
|
</div> |
||||
|
</Layout> |
||||
|
</div> |
||||
|
|
||||
|
<div class="template-categories"> |
||||
|
<Layout gap="XL" noPadding> |
||||
|
{#each filteredTemplateCategories as templateCategoryKey} |
||||
|
<div class="template-category" data-cy={templateCategoryKey}> |
||||
|
<Detail size="M">{templateCategoryKey}</Detail> |
||||
|
<div class="template-grid"> |
||||
|
{#each filteredTemplates[templateCategoryKey] as templateEntry} |
||||
|
<TemplateCard |
||||
|
name={templateEntry.name} |
||||
|
imageSrc={templateEntry.image} |
||||
|
backgroundColour={templateEntry.background} |
||||
|
icon={templateEntry.icon} |
||||
|
> |
||||
|
<Button |
||||
|
cta |
||||
|
on:click={() => { |
||||
|
template = templateEntry |
||||
|
creationModal.show() |
||||
|
}} |
||||
|
> |
||||
|
Use template |
||||
|
</Button> |
||||
|
<a |
||||
|
href={templateEntry.url} |
||||
|
target="_blank" |
||||
|
class="overlay-preview-link spectrum-Button spectrum-Button--sizeM spectrum-Button--secondary" |
||||
|
on:click|stopPropagation |
||||
|
> |
||||
|
Details |
||||
|
</a> |
||||
|
</TemplateCard> |
||||
|
{/each} |
||||
|
</div> |
||||
|
</div> |
||||
|
{/each} |
||||
|
</Layout> |
||||
|
</div> |
||||
|
|
||||
|
<Modal |
||||
|
bind:this={creationModal} |
||||
|
padding={false} |
||||
|
width="600px" |
||||
|
on:hide={stopAppCreation} |
||||
|
> |
||||
|
<CreateAppModal {template} /> |
||||
|
</Modal> |
||||
|
|
||||
|
<style> |
||||
|
.template-grid { |
||||
|
padding-top: 10px; |
||||
|
display: grid; |
||||
|
grid-gap: var(--spacing-xl); |
||||
|
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); |
||||
|
} |
||||
|
|
||||
|
a:hover.spectrum-Button.spectrum-Button--secondary.overlay-preview-link { |
||||
|
background-color: #c8c8c8; |
||||
|
border-color: #c8c8c8; |
||||
|
color: #505050; |
||||
|
} |
||||
|
|
||||
|
a.spectrum-Button--secondary.overlay-preview-link { |
||||
|
margin-top: 20px; |
||||
|
border-color: #c8c8c8; |
||||
|
color: #c8c8c8; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,151 @@ |
|||||
|
<script> |
||||
|
import { goto } from "@roxi/routify" |
||||
|
import { |
||||
|
Layout, |
||||
|
Page, |
||||
|
notifications, |
||||
|
Button, |
||||
|
Heading, |
||||
|
Body, |
||||
|
Modal, |
||||
|
Divider, |
||||
|
} from "@budibase/bbui" |
||||
|
import CreateAppModal from "components/start/CreateAppModal.svelte" |
||||
|
import TemplateDisplay from "components/common/TemplateDisplay.svelte" |
||||
|
import { onMount } from "svelte" |
||||
|
import { templates } from "stores/portal" |
||||
|
|
||||
|
let loaded = false |
||||
|
let template |
||||
|
let creationModal = false |
||||
|
let creatingApp = false |
||||
|
|
||||
|
const welcomeBody = |
||||
|
"Start from scratch or get a head start with one of our templates" |
||||
|
const createAppTitle = "Create new app" |
||||
|
const createAppButtonText = "Start from scratch" |
||||
|
|
||||
|
onMount(async () => { |
||||
|
try { |
||||
|
await templates.load() |
||||
|
if ($templates?.length === 0) { |
||||
|
notifications.error( |
||||
|
"There was a problem loading quick start templates." |
||||
|
) |
||||
|
} |
||||
|
} catch (error) { |
||||
|
notifications.error("Error loading apps and templates") |
||||
|
} |
||||
|
loaded = true |
||||
|
}) |
||||
|
|
||||
|
const initiateAppCreation = () => { |
||||
|
template = null |
||||
|
creationModal.show() |
||||
|
creatingApp = true |
||||
|
} |
||||
|
|
||||
|
const stopAppCreation = () => { |
||||
|
template = null |
||||
|
creatingApp = false |
||||
|
} |
||||
|
|
||||
|
const initiateAppImport = () => { |
||||
|
template = { fromFile: true } |
||||
|
creationModal.show() |
||||
|
creatingApp = true |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<Page wide> |
||||
|
<Layout noPadding gap="XL"> |
||||
|
<span> |
||||
|
<Button |
||||
|
quiet |
||||
|
secondary |
||||
|
icon={"ChevronLeft"} |
||||
|
on:click={() => { |
||||
|
$goto("../") |
||||
|
}} |
||||
|
> |
||||
|
Back |
||||
|
</Button> |
||||
|
</span> |
||||
|
|
||||
|
<div class="title"> |
||||
|
<div class="welcome"> |
||||
|
<Layout noPadding gap="XS"> |
||||
|
<Heading size="M">{createAppTitle}</Heading> |
||||
|
<Body size="S"> |
||||
|
{welcomeBody} |
||||
|
</Body> |
||||
|
</Layout> |
||||
|
|
||||
|
<div class="buttons"> |
||||
|
<Button |
||||
|
dataCy="create-app-btn" |
||||
|
size="L" |
||||
|
icon="Add" |
||||
|
cta |
||||
|
on:click={initiateAppCreation} |
||||
|
> |
||||
|
{createAppButtonText} |
||||
|
</Button> |
||||
|
<Button |
||||
|
dataCy="import-app-btn" |
||||
|
icon="Import" |
||||
|
size="L" |
||||
|
quiet |
||||
|
secondary |
||||
|
on:click={initiateAppImport} |
||||
|
> |
||||
|
Import app |
||||
|
</Button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<Divider size="S" /> |
||||
|
|
||||
|
{#if loaded && $templates?.length} |
||||
|
<TemplateDisplay templates={$templates} /> |
||||
|
{/if} |
||||
|
</Layout> |
||||
|
</Page> |
||||
|
|
||||
|
<Modal |
||||
|
bind:this={creationModal} |
||||
|
padding={false} |
||||
|
width="600px" |
||||
|
on:hide={stopAppCreation} |
||||
|
> |
||||
|
<CreateAppModal {template} /> |
||||
|
</Modal> |
||||
|
|
||||
|
<style> |
||||
|
.title .welcome > .buttons { |
||||
|
padding-top: 30px; |
||||
|
} |
||||
|
.title { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
gap: var(--spacing-xl); |
||||
|
flex-wrap: wrap; |
||||
|
} |
||||
|
.buttons { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
justify-content: flex-start; |
||||
|
align-items: center; |
||||
|
gap: var(--spacing-xl); |
||||
|
flex-wrap: wrap; |
||||
|
} |
||||
|
@media (max-width: 640px) { |
||||
|
.buttons { |
||||
|
flex-direction: row-reverse; |
||||
|
justify-content: flex-end; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,43 @@ |
|||||
|
<script> |
||||
|
import { goto } from "@roxi/routify" |
||||
|
import { Layout, Page, notifications, Button } from "@budibase/bbui" |
||||
|
import TemplateDisplay from "components/common/TemplateDisplay.svelte" |
||||
|
import { onMount } from "svelte" |
||||
|
import { templates } from "stores/portal" |
||||
|
|
||||
|
let loaded = false |
||||
|
|
||||
|
onMount(async () => { |
||||
|
try { |
||||
|
await templates.load() |
||||
|
if ($templates?.length === 0) { |
||||
|
notifications.error( |
||||
|
"There was a problem loading quick start templates." |
||||
|
) |
||||
|
} |
||||
|
} catch (error) { |
||||
|
notifications.error("Error loading apps and templates") |
||||
|
} |
||||
|
loaded = true |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<Page wide> |
||||
|
<Layout noPadding gap="XL"> |
||||
|
<span> |
||||
|
<Button |
||||
|
quiet |
||||
|
secondary |
||||
|
icon={"ChevronLeft"} |
||||
|
on:click={() => { |
||||
|
$goto("../") |
||||
|
}} |
||||
|
> |
||||
|
Back |
||||
|
</Button> |
||||
|
</span> |
||||
|
{#if loaded && $templates?.length} |
||||
|
<TemplateDisplay templates={$templates} /> |
||||
|
{/if} |
||||
|
</Layout> |
||||
|
</Page> |
||||
Loading…
Reference in new issue