164 lines
4.0 KiB
Svelte
164 lines
4.0 KiB
Svelte
<script>
|
|
import {
|
|
ModalContent,
|
|
Input,
|
|
notifications,
|
|
Layout,
|
|
keepOpen,
|
|
} from "@budibase/bbui"
|
|
import { createValidationStore } from "helpers/validation/yup"
|
|
import { writable, get } from "svelte/store"
|
|
import * as appValidation from "helpers/validation/yup/app"
|
|
import { appsStore, auth } from "stores/portal"
|
|
import { onMount } from "svelte"
|
|
import { API } from "api"
|
|
import { sdk } from "@budibase/shared-core"
|
|
|
|
export let appId
|
|
export let appName
|
|
export let onDuplicateSuccess = () => {}
|
|
|
|
const validation = createValidationStore()
|
|
const values = writable({ name: appName + " copy", url: null })
|
|
const appPrefix = "/app"
|
|
|
|
let defaultAppName = appName + " copy"
|
|
let duplicating = false
|
|
|
|
$: {
|
|
const { url } = $values
|
|
|
|
validation.check({
|
|
...$values,
|
|
url: url?.[0] === "/" ? url.substring(1, url.length) : url,
|
|
})
|
|
}
|
|
|
|
const resolveAppName = name => {
|
|
return name ? name.trim() : null
|
|
}
|
|
|
|
const resolveAppUrl = name => {
|
|
let parsedName
|
|
const resolvedName = resolveAppName(name)
|
|
parsedName = resolvedName ? resolvedName.toLowerCase() : ""
|
|
const parsedUrl = parsedName ? parsedName.replace(/\s+/g, "-") : ""
|
|
return encodeURI(parsedUrl)
|
|
}
|
|
|
|
const nameToUrl = appName => {
|
|
let resolvedUrl = resolveAppUrl(appName)
|
|
tidyUrl(resolvedUrl)
|
|
}
|
|
|
|
const tidyUrl = url => {
|
|
if (url && !url.startsWith("/")) {
|
|
url = `/${url}`
|
|
}
|
|
$values.url = url === "" ? null : url
|
|
}
|
|
|
|
const duplicateApp = async () => {
|
|
duplicating = true
|
|
|
|
let data = new FormData()
|
|
data.append("name", $values.name.trim())
|
|
if ($values.url) {
|
|
data.append("url", $values.url.trim())
|
|
}
|
|
|
|
try {
|
|
const app = await API.duplicateApp(appId, data)
|
|
appsStore.load()
|
|
if (!sdk.users.isBuilder($auth.user, app?.duplicateAppId)) {
|
|
// Refresh for access to created applications
|
|
await auth.getSelf()
|
|
}
|
|
onDuplicateSuccess()
|
|
notifications.success("App duplicated successfully")
|
|
} catch (err) {
|
|
notifications.error("Error duplicating app")
|
|
duplicating = false
|
|
}
|
|
}
|
|
|
|
const setupValidation = async () => {
|
|
const applications = get(appsStore).apps
|
|
appValidation.name(validation, { apps: applications })
|
|
appValidation.url(validation, { apps: applications })
|
|
|
|
const { url } = $values
|
|
validation.check({
|
|
...$values,
|
|
url: url?.[0] === "/" ? url.substring(1, url.length) : url,
|
|
})
|
|
}
|
|
|
|
$: appUrl = `${window.location.origin}${
|
|
$values.url
|
|
? `${appPrefix}${$values.url}`
|
|
: `${appPrefix}${resolveAppUrl($values.name)}`
|
|
}`
|
|
|
|
onMount(async () => {
|
|
nameToUrl($values.name)
|
|
await setupValidation()
|
|
})
|
|
</script>
|
|
|
|
<ModalContent
|
|
title={"Duplicate App"}
|
|
onConfirm={async () => {
|
|
validation.check({
|
|
...$values,
|
|
})
|
|
if ($validation.valid) {
|
|
await duplicateApp()
|
|
} else {
|
|
return keepOpen
|
|
}
|
|
}}
|
|
>
|
|
<Layout gap="S" noPadding>
|
|
<Input
|
|
autofocus={true}
|
|
bind:value={$values.name}
|
|
disabled={duplicating}
|
|
error={$validation.touched.name && $validation.errors.name}
|
|
on:blur={() => ($validation.touched.name = true)}
|
|
on:change={nameToUrl($values.name)}
|
|
label="Name"
|
|
placeholder={defaultAppName}
|
|
/>
|
|
<span>
|
|
<Input
|
|
bind:value={$values.url}
|
|
disabled={duplicating}
|
|
error={$validation.touched.url && $validation.errors.url}
|
|
on:blur={() => ($validation.touched.url = true)}
|
|
on:change={tidyUrl($values.url)}
|
|
label="URL"
|
|
placeholder={$values.url
|
|
? $values.url
|
|
: `/${resolveAppUrl($values.name)}`}
|
|
/>
|
|
{#if $values.url && $values.url !== "" && !$validation.errors.url}
|
|
<div class="app-server" title={appUrl}>
|
|
{appUrl}
|
|
</div>
|
|
{/if}
|
|
</span>
|
|
</Layout>
|
|
</ModalContent>
|
|
|
|
<style>
|
|
.app-server {
|
|
color: var(--spectrum-global-color-gray-600);
|
|
margin-top: 10px;
|
|
width: 320px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
</style>
|