Update spacing, borders and sizing

This commit is contained in:
Andrew Kingston 2023-02-28 12:29:13 +00:00
parent dd08845a44
commit 862ba6ce92
5 changed files with 202 additions and 166 deletions

View File

@ -33,6 +33,8 @@
export let sort = false export let sort = false
export let fetchTerm = null export let fetchTerm = null
export let customPopoverHeight export let customPopoverHeight
export let align = "left"
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
let searchTerm = null let searchTerm = null
@ -131,7 +133,7 @@
<Popover <Popover
anchor={button} anchor={button}
align="left" align={align || "left"}
bind:this={popover} bind:this={popover}
{open} {open}
on:close={() => (open = false)} on:close={() => (open = false)}

View File

@ -18,6 +18,7 @@
export let autoWidth = false export let autoWidth = false
export let autocomplete = false export let autocomplete = false
export let sort = false export let sort = false
export let align
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
@ -66,6 +67,7 @@
{fieldColour} {fieldColour}
{options} {options}
{autoWidth} {autoWidth}
{align}
{getOptionLabel} {getOptionLabel}
{getOptionValue} {getOptionValue}
{getOptionIcon} {getOptionIcon}

View File

@ -22,6 +22,7 @@
export let tooltip = "" export let tooltip = ""
export let autocomplete = false export let autocomplete = false
export let customPopoverHeight export let customPopoverHeight
export let align
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const onChange = e => { const onChange = e => {
@ -48,6 +49,7 @@
{placeholder} {placeholder}
{autoWidth} {autoWidth}
{sort} {sort}
{align}
{getOptionLabel} {getOptionLabel}
{getOptionValue} {getOptionValue}
{getOptionIcon} {getOptionIcon}

View File

@ -11,6 +11,8 @@
export let quiet = false export let quiet = false
export let allowPublic = true export let allowPublic = true
export let allowRemove = false export let allowRemove = false
export let disabled = false
export let align
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const RemoveID = "remove" const RemoveID = "remove"
@ -59,6 +61,8 @@
<Select <Select
{autoWidth} {autoWidth}
{quiet} {quiet}
{disabled}
{align}
bind:value bind:value
on:change={onChange} on:change={onChange}
{options} {options}

View File

@ -81,7 +81,16 @@
await usersFetch.refresh() await usersFetch.refresh()
filteredUsers = $usersFetch.rows.map(user => { filteredUsers = $usersFetch.rows.map(user => {
const appRole = Object.keys(user.roles).find(x => x === prodAppId) const isBuilderOrAdmin = user.admin?.global || user.builder?.global
let role = undefined
if (isBuilderOrAdmin) {
role = "ADMIN"
} else {
const appRole = Object.keys(user.roles).find(x => x === prodAppId)
if (appRole) {
role = user.roles[appRole]
}
}
// if ( // if (
// !appRole && // !appRole &&
@ -94,7 +103,8 @@
return { return {
...user, ...user,
role: !appRole ? undefined : user.roles[appRole], role,
isBuilderOrAdmin,
} }
}) })
} }
@ -423,167 +433,172 @@
</span> </span>
</div> </div>
{#if promptInvite && !userOnboardResponse} <div class="body">
<Layout gap="S" paddingX="XL"> {#if promptInvite && !userOnboardResponse}
<div class="invite-header"> <Layout gap="S" paddingX="XL">
<Heading size="XS">No user found</Heading> <div class="invite-header">
<div class="invite-directions"> <Heading size="XS">No user found</Heading>
Add a valid email to invite a new user <div class="invite-directions">
</div> Add a valid email to invite a new user
</div>
<div class="invite-form">
<span>{query || ""}</span>
<ActionButton
icon="UserAdd"
disabled={!queryIsEmail || inviting}
on:click={onInviteUser}
>
Add user
</ActionButton>
</div>
</Layout>
{/if}
{#if !promptInvite}
<Layout gap="L" noPadding>
{#if filteredInvites?.length}
<Layout noPadding gap="XS">
<div class="auth-entity-header">
<div class="auth-entity-title">Pending invites</div>
<div class="auth-entity-access-title">Access</div>
</div> </div>
{#each filteredInvites as invite}
<div class="auth-entity">
<div class="details">
<div class="user-email" title={invite.email}>
{invite.email}
</div>
</div>
<div class="auth-entity-access">
<RoleSelect
placeholder={false}
value={invite.info.apps?.[prodAppId]}
allowRemove={invite.info.apps?.[prodAppId]}
allowPublic={false}
quiet={true}
on:change={e => {
onUpdateUserInvite(invite, e.detail)
}}
on:remove={() => {
onUninviteAppUser(invite)
}}
/>
</div>
</div>
{/each}
</Layout>
{/if}
{#if $licensing.groupsEnabled && filteredGroups?.length}
<Layout noPadding gap="XS">
<div class="auth-entity-header">
<div class="auth-entity-title">Groups</div>
<div class="auth-entity-access-title">Access</div>
</div>
{#each filteredGroups as group}
<div
class="auth-entity group"
on:click={() => {
if (selectedGroup != group._id) {
selectedGroup = group._id
} else {
selectedGroup = null
}
}}
on:keydown={() => {}}
>
<div class="details">
<GroupIcon {group} size="S" />
<div>
{group.name}
</div>
<div class="auth-entity-meta">
{`${group.users?.length} user${
group.users?.length != 1 ? "s" : ""
}`}
</div>
</div>
<div class="auth-entity-access">
<RoleSelect
placeholder={false}
value={group.role}
allowRemove={group.role}
allowPublic={false}
quiet={true}
on:change={e => {
onUpdateGroup(group, e.detail)
}}
on:remove={() => {
onUpdateGroup(group)
}}
/>
</div>
</div>
{/each}
</Layout>
{/if}
{#if filteredUsers?.length}
<div class="auth-entity-section">
<div class="auth-entity-header ">
<div class="auth-entity-title">Users</div>
<div class="auth-entity-access-title">Access</div>
</div>
{#each allUsers as user}
<div class="auth-entity">
<div class="details">
<div class="user-email" title={user.email}>
{user.email}
</div>
<div class="auth-entity-meta">
{userTitle(user)}
</div>
</div>
<div class="auth-entity-access" class:muted={user.group}>
<RoleSelect
note={roleNote(user)}
placeholder={false}
value={user.role}
allowRemove={user.role && !user.group}
allowPublic={false}
quiet={true}
on:change={e => {
onUpdateUser(user, e.detail)
}}
on:remove={() => {
onUpdateUser(user)
}}
/>
</div>
</div>
{/each}
</div> </div>
{/if} <div class="invite-form">
</Layout> <span>{query || ""}</span>
{/if} <ActionButton
icon="UserAdd"
{#if userOnboardResponse?.created} disabled={!queryIsEmail || inviting}
<Layout gap="S" paddingX="XL"> on:click={onInviteUser}
<div class="invite-header"> >
<Heading size="XS">User added!</Heading> Add user
<div class="invite-directions"> </ActionButton>
Email invites are not available without SMTP configuration. Here is
the password that has been generated for this user.
</div> </div>
</div> </Layout>
<div> {/if}
<CopyInput
value={userOnboardResponse.successful[0]?.password} {#if !promptInvite}
label="Password" <Layout gap="L" noPadding>
/> {#if filteredInvites?.length}
</div> <Layout noPadding gap="XS">
</Layout> <div class="auth-entity-header">
{/if} <div class="auth-entity-title">Pending invites</div>
<div class="auth-entity-access-title">Access</div>
</div>
{#each filteredInvites as invite}
<div class="auth-entity">
<div class="details">
<div class="user-email" title={invite.email}>
{invite.email}
</div>
</div>
<div class="auth-entity-access">
<RoleSelect
placeholder={false}
value={invite.info.apps?.[prodAppId]}
allowRemove={invite.info.apps?.[prodAppId]}
allowPublic={false}
quiet={true}
on:change={e => {
onUpdateUserInvite(invite, e.detail)
}}
on:remove={() => {
onUninviteAppUser(invite)
}}
/>
</div>
</div>
{/each}
</Layout>
{/if}
{#if $licensing.groupsEnabled && filteredGroups?.length}
<Layout noPadding gap="XS">
<div class="auth-entity-header">
<div class="auth-entity-title">Groups</div>
<div class="auth-entity-access-title">Access</div>
</div>
{#each filteredGroups as group}
<div
class="auth-entity group"
on:click={() => {
if (selectedGroup != group._id) {
selectedGroup = group._id
} else {
selectedGroup = null
}
}}
on:keydown={() => {}}
>
<div class="details">
<GroupIcon {group} size="S" />
<div>
{group.name}
</div>
<div class="auth-entity-meta">
{`${group.users?.length} user${
group.users?.length != 1 ? "s" : ""
}`}
</div>
</div>
<div class="auth-entity-access">
<RoleSelect
placeholder={false}
value={group.role}
allowRemove={group.role}
allowPublic={false}
quiet={true}
on:change={e => {
onUpdateGroup(group, e.detail)
}}
on:remove={() => {
onUpdateGroup(group)
}}
/>
</div>
</div>
{/each}
</Layout>
{/if}
{#if filteredUsers?.length}
<div class="auth-entity-section">
<div class="auth-entity-header ">
<div class="auth-entity-title">Users</div>
<div class="auth-entity-access-title">Access</div>
</div>
{#each allUsers as user}
<div class="auth-entity">
<div class="details">
<div class="user-email" title={user.email}>
{user.email}
</div>
<div class="auth-entity-meta">
{userTitle(user)}
</div>
</div>
<div class="auth-entity-access" class:muted={user.group}>
<RoleSelect
note={roleNote(user)}
placeholder={false}
value={user.role}
allowRemove={user.role && !user.group}
allowPublic={false}
quiet={true}
on:change={e => {
onUpdateUser(user, e.detail)
}}
on:remove={() => {
onUpdateUser(user)
}}
disabled={user.isBuilderOrAdmin}
autoWidth
align="right"
/>
</div>
</div>
{/each}
</div>
{/if}
</Layout>
{/if}
{#if userOnboardResponse?.created}
<Layout gap="S" paddingX="XL">
<div class="invite-header">
<Heading size="XS">User added!</Heading>
<div class="invite-directions">
Email invites are not available without SMTP configuration. Here is
the password that has been generated for this user.
</div>
</div>
<div>
<CopyInput
value={userOnboardResponse.successful[0]?.password}
label="Password"
/>
</div>
</Layout>
{/if}
</div>
</div> </div>
<style> <style>
@ -617,6 +632,9 @@
white-space: nowrap; white-space: nowrap;
} }
.auth-entity-access {
margin-right: var(--spacing-m);
}
.auth-entity-access.muted :global(.spectrum-Picker-label), .auth-entity-access.muted :global(.spectrum-Picker-label),
.auth-entity-access.muted :global(.spectrum-StatusLight) { .auth-entity-access.muted :global(.spectrum-StatusLight) {
opacity: 0.7; opacity: 0.7;
@ -634,7 +652,7 @@
.auth-entity, .auth-entity,
.auth-entity-header { .auth-entity-header {
display: grid; display: grid;
grid-template-columns: 65% auto; grid-template-columns: 1fr 100px;
align-items: center; align-items: center;
gap: var(--spacing-xl); gap: var(--spacing-xl);
} }
@ -657,10 +675,8 @@
background: var(--background); background: var(--background);
border-left: var(--border-light); border-left: var(--border-light);
z-index: 3; z-index: 3;
padding: var(--spacing-xl) 0px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--spacing-xl);
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
transition: transform 130ms ease-out; transition: transform 130ms ease-out;
@ -689,8 +705,10 @@
#builder-side-panel-container .search { #builder-side-panel-container .search {
padding-top: var(--spacing-m); padding-top: var(--spacing-m);
padding-bottom: var(--spacing-m); padding-bottom: var(--spacing-m);
border-top: 1px solid var(--spectrum-alias-border-color-mid); border-top: var(--border-light);
border-bottom: 1px solid var(--spectrum-alias-border-color-mid); border-bottom: var(--border-light);
border-left: 2px solid transparent;
border-right: 2px solid transparent;
} }
#builder-side-panel-container .search :global(input) { #builder-side-panel-container .search :global(input) {
@ -721,6 +739,7 @@
} }
.builder-side-panel-header { .builder-side-panel-header {
height: 58px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
@ -732,4 +751,11 @@
gap: var(--spacing-s); gap: var(--spacing-s);
flex-direction: column; flex-direction: column;
} }
.body {
display: flex;
flex-direction: column;
gap: var(--spacing-xl);
padding: var(--spacing-xl) 0;
}
</style> </style>