Backups UI Changes (#9726)
* Backups UI Changes * PR Feedback --------- Co-authored-by: Rory Powell <rory.codes@gmail.com>
This commit is contained in:
parent
faaf01cd53
commit
e7f8a8a801
|
@ -0,0 +1,13 @@
|
|||
const getUserInitials = user => {
|
||||
if (user.firstName && user.lastName) {
|
||||
return user.firstName[0] + user.lastName[0]
|
||||
} else if (user.firstName) {
|
||||
return user.firstName[0]
|
||||
} else if (user.email) {
|
||||
return user.email[0]
|
||||
}
|
||||
|
||||
return "U"
|
||||
}
|
||||
|
||||
export default getUserInitials
|
|
@ -3,31 +3,26 @@
|
|||
ActionMenu,
|
||||
MenuItem,
|
||||
Icon,
|
||||
Input,
|
||||
Heading,
|
||||
Body,
|
||||
Modal,
|
||||
} from "@budibase/bbui"
|
||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||
import CreateRestoreModal from "./CreateRestoreModal.svelte"
|
||||
import { createEventDispatcher, onMount } from "svelte"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
|
||||
export let row
|
||||
|
||||
let deleteDialog
|
||||
let restoreDialog
|
||||
let updateDialog
|
||||
let name
|
||||
let restoreBackupModal
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const onClickRestore = name => {
|
||||
const onClickRestore = () => {
|
||||
dispatch("buttonclick", {
|
||||
type: "backupRestore",
|
||||
name,
|
||||
backupId: row._id,
|
||||
restoreBackupName: name,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -38,21 +33,9 @@
|
|||
})
|
||||
}
|
||||
|
||||
const onClickUpdate = () => {
|
||||
dispatch("buttonclick", {
|
||||
type: "backupUpdate",
|
||||
backupId: row._id,
|
||||
name,
|
||||
})
|
||||
}
|
||||
|
||||
async function downloadExport() {
|
||||
window.open(`/api/apps/${row.appId}/backups/${row._id}/file`, "_blank")
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
name = row.name
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="cell">
|
||||
|
@ -66,12 +49,11 @@
|
|||
<MenuItem on:click={deleteDialog.show} icon="Delete">Delete</MenuItem>
|
||||
<MenuItem on:click={downloadExport} icon="Download">Download</MenuItem>
|
||||
{/if}
|
||||
<MenuItem on:click={updateDialog.show} icon="Edit">Rename</MenuItem>
|
||||
</ActionMenu>
|
||||
</div>
|
||||
|
||||
<Modal bind:this={restoreBackupModal}>
|
||||
<CreateRestoreModal confirm={name => onClickRestore(name)} />
|
||||
<CreateRestoreModal confirm={onClickRestore} />
|
||||
</Modal>
|
||||
|
||||
<ConfirmDialog
|
||||
|
@ -80,9 +62,7 @@
|
|||
onOk={onClickDelete}
|
||||
title="Confirm Deletion"
|
||||
>
|
||||
Are you sure you wish to delete the backup
|
||||
<i>{row.name}?</i>
|
||||
This action cannot be undone.
|
||||
Are you sure you wish to delete this backup? This action cannot be undone.
|
||||
</ConfirmDialog>
|
||||
|
||||
<ConfirmDialog
|
||||
|
@ -92,21 +72,10 @@
|
|||
title="Confirm restore"
|
||||
warning={false}
|
||||
>
|
||||
<Heading size="S">{row.name || "Backup"}</Heading>
|
||||
<Heading size="S">Backup</Heading>
|
||||
<Body size="S">{new Date(row.timestamp).toLocaleString()}</Body>
|
||||
</ConfirmDialog>
|
||||
|
||||
<ConfirmDialog
|
||||
bind:this={updateDialog}
|
||||
disabled={!name}
|
||||
okText="Confirm"
|
||||
onOk={onClickUpdate}
|
||||
title="Update Backup"
|
||||
warning={false}
|
||||
>
|
||||
<Input onlabel="Backup name" bind:value={name} />
|
||||
</ConfirmDialog>
|
||||
|
||||
<style>
|
||||
.cell {
|
||||
display: flex;
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
<script>
|
||||
import { ModalContent, Input } from "@budibase/bbui"
|
||||
import { auth } from "stores/portal"
|
||||
|
||||
export let createManualBackup
|
||||
|
||||
let templateName = $auth.user.firstName
|
||||
? `${$auth.user.firstName}'s Backup`
|
||||
: "New Backup"
|
||||
let name = templateName
|
||||
</script>
|
||||
|
||||
<ModalContent
|
||||
onConfirm={() => createManualBackup(name)}
|
||||
title="Create new backup"
|
||||
diabled={!name}
|
||||
confirmText="Create"
|
||||
><Input label="Backup name" bind:value={name} /></ModalContent
|
||||
>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -1,8 +0,0 @@
|
|||
<script>
|
||||
import { truncate } from "lodash"
|
||||
|
||||
export let value
|
||||
$: truncatedValue = truncate(value, { length: 12 })
|
||||
</script>
|
||||
|
||||
{truncatedValue}
|
|
@ -0,0 +1,10 @@
|
|||
<script>
|
||||
import dayjs from "dayjs"
|
||||
import relativeTime from "dayjs/plugin/relativeTime"
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
export let value
|
||||
</script>
|
||||
|
||||
<span title={value}>{dayjs(value).fromNow()}</span>
|
|
@ -1,17 +1,14 @@
|
|||
<script>
|
||||
import getUserInitials from "helpers/userInitials.js"
|
||||
import { Avatar } from "@budibase/bbui"
|
||||
|
||||
export let value
|
||||
|
||||
let firstName = value?.firstName
|
||||
let lastName = value?.lastName || ""
|
||||
|
||||
$: username =
|
||||
firstName && lastName ? `${firstName} ${lastName}` : value?.email
|
||||
$: initials = getUserInitials(value)
|
||||
</script>
|
||||
|
||||
<div class="cell">
|
||||
{#if value != null}
|
||||
<div>{username}</div>
|
||||
{/if}
|
||||
<div title={value.email} class="cell">
|
||||
<Avatar size="M" {initials} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
DatePicker,
|
||||
Divider,
|
||||
Layout,
|
||||
Modal,
|
||||
notifications,
|
||||
Pagination,
|
||||
Select,
|
||||
|
@ -16,25 +15,22 @@
|
|||
} from "@budibase/bbui"
|
||||
import { backups, licensing, auth, admin, overview } from "stores/portal"
|
||||
import { createPaginationStore } from "helpers/pagination"
|
||||
import DateRenderer from "components/common/renderers/DateTimeRenderer.svelte"
|
||||
import TimeAgoRenderer from "./_components/TimeAgoRenderer.svelte"
|
||||
import AppSizeRenderer from "./_components/AppSizeRenderer.svelte"
|
||||
import CreateBackupModal from "./_components/CreateBackupModal.svelte"
|
||||
import ActionsRenderer from "./_components/ActionsRenderer.svelte"
|
||||
import UserRenderer from "./_components/UserRenderer.svelte"
|
||||
import StatusRenderer from "./_components/StatusRenderer.svelte"
|
||||
import TypeRenderer from "./_components/TypeRenderer.svelte"
|
||||
import NameRenderer from "./_components/NameRenderer.svelte"
|
||||
import BackupsDefault from "assets/backups-default.png"
|
||||
import { BackupTrigger, BackupType } from "constants/backend/backups"
|
||||
import { onMount } from "svelte"
|
||||
|
||||
let loading = true
|
||||
let backupData = null
|
||||
let modal
|
||||
let pageInfo = createPaginationStore()
|
||||
let filterOpt = null
|
||||
let startDate = null
|
||||
let endDate = null
|
||||
let loaded = false
|
||||
let filters = [
|
||||
{
|
||||
label: "Manual backup",
|
||||
|
@ -44,10 +40,6 @@
|
|||
label: "Published backup",
|
||||
value: { type: BackupType.BACKUP, trigger: BackupTrigger.PUBLISH },
|
||||
},
|
||||
{
|
||||
label: "Scheduled backup",
|
||||
value: { type: BackupType.BACKUP, trigger: BackupTrigger.SCHEDULED },
|
||||
},
|
||||
{
|
||||
label: "Pre-restore backup",
|
||||
value: { type: BackupType.BACKUP, trigger: BackupTrigger.RESTORING },
|
||||
|
@ -71,10 +63,6 @@
|
|||
displayName: "Date",
|
||||
width: "auto",
|
||||
},
|
||||
name: {
|
||||
displayName: "Name",
|
||||
width: "auto",
|
||||
},
|
||||
appSize: {
|
||||
displayName: "App size",
|
||||
width: "auto",
|
||||
|
@ -96,11 +84,10 @@
|
|||
const customRenderers = [
|
||||
{ column: "appSize", component: AppSizeRenderer },
|
||||
{ column: "actions", component: ActionsRenderer },
|
||||
{ column: "createdAt", component: DateRenderer },
|
||||
{ column: "createdAt", component: TimeAgoRenderer },
|
||||
{ column: "createdBy", component: UserRenderer },
|
||||
{ column: "status", component: StatusRenderer },
|
||||
{ column: "type", component: TypeRenderer },
|
||||
{ column: "name", component: NameRenderer },
|
||||
]
|
||||
|
||||
function flattenBackups(backups) {
|
||||
|
@ -126,11 +113,11 @@
|
|||
backupData = flattenBackups(response.data)
|
||||
}
|
||||
|
||||
async function createManualBackup(name) {
|
||||
async function createManualBackup() {
|
||||
try {
|
||||
loading = true
|
||||
let response = await backups.createManualBackup({
|
||||
appId: app.instance._id,
|
||||
name,
|
||||
})
|
||||
await fetchBackups(filterOpt, page)
|
||||
notifications.success(response.message)
|
||||
|
@ -139,6 +126,20 @@
|
|||
}
|
||||
}
|
||||
|
||||
const poll = backupData => {
|
||||
if (backupData === null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (backupData.some(datum => datum.status === "started")) {
|
||||
setTimeout(() => fetchBackups(filterOpt, page), 2000)
|
||||
} else {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
$: poll(backupData)
|
||||
|
||||
async function handleButtonClick({ detail }) {
|
||||
if (detail.type === "backupDelete") {
|
||||
await backups.deleteBackup({
|
||||
|
@ -165,7 +166,7 @@
|
|||
|
||||
onMount(async () => {
|
||||
await fetchBackups(filterOpt, page, startDate, endDate)
|
||||
loaded = true
|
||||
loading = false
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -206,7 +207,7 @@
|
|||
View plans
|
||||
</Button>
|
||||
</div>
|
||||
{:else if !backupData?.length && loaded && !filterOpt && !startDate}
|
||||
{:else if !backupData?.length && !loading && !filterOpt && !startDate}
|
||||
<div class="center">
|
||||
<Layout noPadding gap="S" justifyItems="center">
|
||||
<img height="130px" src={BackupsDefault} alt="BackupsDefault" />
|
||||
|
@ -215,11 +216,13 @@
|
|||
<Body>You can manually back up your app any time</Body>
|
||||
</Layout>
|
||||
<div>
|
||||
<Button on:click={modal.show} cta>Create backup</Button>
|
||||
<Button cta disabled={loading} on:click={createManualBackup}>
|
||||
Create backup
|
||||
</Button>
|
||||
</div>
|
||||
</Layout>
|
||||
</div>
|
||||
{:else if loaded}
|
||||
{:else}
|
||||
<Layout noPadding gap="M" alignContent="start">
|
||||
<div class="controls">
|
||||
<div class="search">
|
||||
|
@ -245,7 +248,9 @@
|
|||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Button cta on:click={modal.show}>Create new backup</Button>
|
||||
<Button cta disabled={loading} on:click={createManualBackup}
|
||||
>Create new backup</Button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table">
|
||||
|
@ -275,10 +280,6 @@
|
|||
{/if}
|
||||
</Layout>
|
||||
|
||||
<Modal bind:this={modal}>
|
||||
<CreateBackupModal {createManualBackup} />
|
||||
</Modal>
|
||||
|
||||
<style>
|
||||
.title {
|
||||
display: flex;
|
||||
|
|
|
@ -2,6 +2,7 @@ import { derived, writable, get } from "svelte/store"
|
|||
import { API } from "api"
|
||||
import { admin } from "stores/portal"
|
||||
import analytics from "analytics"
|
||||
import getUserInitials from "helpers/userInitials.js"
|
||||
|
||||
export function createAuthStore() {
|
||||
const auth = writable({
|
||||
|
@ -18,16 +19,7 @@ export function createAuthStore() {
|
|||
let isBuilder = false
|
||||
if ($store.user) {
|
||||
const user = $store.user
|
||||
if (user.firstName) {
|
||||
initials = user.firstName[0]
|
||||
if (user.lastName) {
|
||||
initials += user.lastName[0]
|
||||
}
|
||||
} else if (user.email) {
|
||||
initials = user.email[0]
|
||||
} else {
|
||||
initials = "Unknown"
|
||||
}
|
||||
initials = getUserInitials(user)
|
||||
isAdmin = !!user.admin?.global
|
||||
isBuilder = !!user.builder?.global
|
||||
}
|
||||
|
|
|
@ -30,8 +30,8 @@ export function createBackupsStore() {
|
|||
return API.deleteBackup({ appId, backupId })
|
||||
}
|
||||
|
||||
async function createManualBackup(appId, name) {
|
||||
return API.createManualBackup(appId, name)
|
||||
async function createManualBackup(appId) {
|
||||
return API.createManualBackup(appId)
|
||||
}
|
||||
|
||||
async function updateBackup({ appId, backupId, name }) {
|
||||
|
|
|
@ -21,10 +21,9 @@ export const buildBackupsEndpoints = API => ({
|
|||
})
|
||||
},
|
||||
|
||||
createManualBackup: async ({ appId, name }) => {
|
||||
createManualBackup: async ({ appId }) => {
|
||||
return await API.post({
|
||||
url: `/api/apps/${appId}/backups`,
|
||||
body: { name },
|
||||
})
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in New Issue