forked from tsai/budibase
11 changed files with 230 additions and 106 deletions
@ -0,0 +1,72 @@ |
|||||
|
<script> |
||||
|
import { Search, Layout, Select } from "@budibase/bbui" |
||||
|
import NavigationPanel from "components/design/NavigationPanel/NavigationPanel.svelte" |
||||
|
import { roles } from "stores/backend" |
||||
|
import { store } from "builderStore" |
||||
|
import NavItem from "components/common/NavItem.svelte" |
||||
|
import ScreenDropdownMenu from "./ScreenDropdownMenu.svelte" |
||||
|
import { RoleColours } from "constants" |
||||
|
import ScreenWizard from "./ScreenWizard.svelte" |
||||
|
let searchString |
||||
|
let accessRole = "all" |
||||
|
let showNewScreenModal |
||||
|
|
||||
|
$: filteredScreens = getFilteredScreens( |
||||
|
$store.screens, |
||||
|
searchString, |
||||
|
accessRole |
||||
|
) |
||||
|
|
||||
|
const getFilteredScreens = (screens, search, role) => { |
||||
|
return screens |
||||
|
.filter(screen => { |
||||
|
const searchMatch = !search || screen.routing.route.includes(search) |
||||
|
const roleMatch = |
||||
|
!role || role === "all" || screen.routing.roleId === role |
||||
|
return searchMatch && roleMatch |
||||
|
}) |
||||
|
.slice() |
||||
|
.sort((a, b) => { |
||||
|
return a.routing.route < b.routing.route ? -1 : 1 |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const getRoleColor = roleId => { |
||||
|
return RoleColours[roleId] || "#ffa500" |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<NavigationPanel |
||||
|
title="Screens" |
||||
|
showAddButton |
||||
|
onClickAddButton={showNewScreenModal} |
||||
|
> |
||||
|
<Layout paddingX="L" paddingY="XL" gap="S"> |
||||
|
<Search |
||||
|
placeholder="Enter a route to search" |
||||
|
value={searchString} |
||||
|
on:change={e => (searchString = e.detail)} |
||||
|
/> |
||||
|
<Select |
||||
|
bind:value={accessRole} |
||||
|
placeholder={null} |
||||
|
getOptionLabel={role => role.name} |
||||
|
getOptionValue={role => role._id} |
||||
|
options={[{ name: "All screens", _id: "all" }, ...$roles]} |
||||
|
/> |
||||
|
</Layout> |
||||
|
{#each filteredScreens as screen (screen._id)} |
||||
|
<NavItem |
||||
|
icon={screen.routing.homeScreen ? "Home" : "WebPage"} |
||||
|
indentLevel={0} |
||||
|
selected={$store.selectedScreenId === screen._id} |
||||
|
text={screen.routing.route} |
||||
|
on:click={() => ($store.selectedScreenId = screen._id)} |
||||
|
color={getRoleColor(screen.routing.roleId)} |
||||
|
> |
||||
|
<ScreenDropdownMenu screenId={screen._id} /> |
||||
|
</NavItem> |
||||
|
{/each} |
||||
|
</NavigationPanel> |
||||
|
|
||||
|
<ScreenWizard bind:showModal={showNewScreenModal} /> |
||||
@ -0,0 +1,146 @@ |
|||||
|
<script> |
||||
|
import { selectedScreen } from "builderStore" |
||||
|
import SettingsPanel from "components/design/SettingsPanel/SettingsPanel.svelte" |
||||
|
import { get } from "svelte/store" |
||||
|
import { get as deepGet, setWith } from "lodash" |
||||
|
import { |
||||
|
Input, |
||||
|
Layout, |
||||
|
Button, |
||||
|
Toggle, |
||||
|
Checkbox, |
||||
|
notifications, |
||||
|
} from "@budibase/bbui" |
||||
|
import PropertyControl from "components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte" |
||||
|
import RoleSelect from "components/design/PropertiesPanel/PropertyControls/RoleSelect.svelte" |
||||
|
import { currentAsset, store, selectedAccessRole } from "builderStore" |
||||
|
import { FrontendTypes } from "constants" |
||||
|
import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl" |
||||
|
|
||||
|
let errors = {} |
||||
|
|
||||
|
const routeTaken = url => { |
||||
|
const roleId = get(selectedAccessRole) || "BASIC" |
||||
|
return get(store).screens.some( |
||||
|
screen => |
||||
|
screen.routing.route.toLowerCase() === url.toLowerCase() && |
||||
|
screen.routing.roleId === roleId |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
const roleTaken = roleId => { |
||||
|
const url = get(currentAsset)?.routing.route |
||||
|
return get(store).screens.some( |
||||
|
screen => |
||||
|
screen.routing.route.toLowerCase() === url.toLowerCase() && |
||||
|
screen.routing.roleId === roleId |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
const setAssetProps = (name, value, parser, validate) => { |
||||
|
if (parser) { |
||||
|
value = parser(value) |
||||
|
} |
||||
|
if (validate) { |
||||
|
const error = validate(value) |
||||
|
errors = { |
||||
|
...errors, |
||||
|
[name]: error, |
||||
|
} |
||||
|
if (error) { |
||||
|
return |
||||
|
} |
||||
|
} else { |
||||
|
errors = { |
||||
|
...errors, |
||||
|
[name]: null, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const selectedAsset = get(currentAsset) |
||||
|
store.update(state => { |
||||
|
if ( |
||||
|
name === "_instanceName" && |
||||
|
state.currentFrontEndType === FrontendTypes.SCREEN |
||||
|
) { |
||||
|
selectedAsset.props._instanceName = value |
||||
|
} else { |
||||
|
setWith(selectedAsset, name.split("."), value, Object) |
||||
|
} |
||||
|
return state |
||||
|
}) |
||||
|
|
||||
|
try { |
||||
|
store.actions.preview.saveSelected() |
||||
|
} catch (error) { |
||||
|
notifications.error("Error saving settings") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const screenSettings = [ |
||||
|
{ |
||||
|
key: "routing.homeScreen", |
||||
|
control: Checkbox, |
||||
|
props: { |
||||
|
text: "Set as home screen", |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
key: "routing.route", |
||||
|
label: "Route", |
||||
|
control: Input, |
||||
|
parser: val => { |
||||
|
if (!val.startsWith("/")) { |
||||
|
val = "/" + val |
||||
|
} |
||||
|
return sanitizeUrl(val) |
||||
|
}, |
||||
|
validate: val => { |
||||
|
const exisingValue = get(currentAsset)?.routing.route |
||||
|
if (val !== exisingValue && routeTaken(val)) { |
||||
|
return "That URL is already in use for this role" |
||||
|
} |
||||
|
return null |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
key: "routing.roleId", |
||||
|
label: "Access", |
||||
|
control: RoleSelect, |
||||
|
validate: val => { |
||||
|
const exisingValue = get(currentAsset)?.routing.roleId |
||||
|
if (val !== exisingValue && roleTaken(val)) { |
||||
|
return "That role is already in use for this URL" |
||||
|
} |
||||
|
return null |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
key: "showNavigation", |
||||
|
label: "Navigation", |
||||
|
control: Toggle, |
||||
|
props: { |
||||
|
text: "Show navigation", |
||||
|
}, |
||||
|
}, |
||||
|
] |
||||
|
</script> |
||||
|
|
||||
|
<SettingsPanel |
||||
|
title={$selectedScreen?.routing.route} |
||||
|
icon={$selectedScreen.routing.route === "/" ? "Home" : "WebPage"} |
||||
|
> |
||||
|
<Layout gap="S" paddingX="L" paddingY="XL"> |
||||
|
{#each screenSettings as def (def.key)} |
||||
|
<PropertyControl |
||||
|
control={def.control} |
||||
|
label={def.label} |
||||
|
key={def.key} |
||||
|
value={deepGet($currentAsset, def.key)} |
||||
|
onChange={val => setAssetProps(def.key, val, def.parser, def.validate)} |
||||
|
props={{ ...def.props, error: errors[def.key] }} |
||||
|
/> |
||||
|
{/each} |
||||
|
<Button cta>View components</Button> |
||||
|
</Layout> |
||||
|
</SettingsPanel> |
||||
Loading…
Reference in new issue