Show request comment (#2525)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-01-20 13:45:43 +06:00 committed by GitHub
parent bea418d46c
commit 42e354f0ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 311 additions and 142 deletions

View File

@ -32,6 +32,7 @@
"@hcengineering/platform": "^0.6.8",
"@hcengineering/model-core": "^0.6.0",
"@hcengineering/model-view": "^0.6.0",
"@hcengineering/model-chunter": "^0.6.0",
"@hcengineering/request": "^0.6.0",
"@hcengineering/activity": "^0.6.0",
"@hcengineering/chunter": "^0.6.2",

View File

@ -18,11 +18,24 @@ import chunter from '@hcengineering/chunter'
import type { EmployeeAccount } from '@hcengineering/contact'
import contact from '@hcengineering/contact'
import { Doc, Domain, IndexKind, Ref, TxCUD } from '@hcengineering/core'
import { ArrOf, Builder, Collection, Index, Model, Prop, ReadOnly, TypeRef, TypeString, UX } from '@hcengineering/model'
import {
ArrOf,
Builder,
Collection,
Index,
Mixin,
Model,
Prop,
ReadOnly,
TypeRef,
TypeString,
UX
} from '@hcengineering/model'
import core, { TAttachedDoc } from '@hcengineering/model-core'
import { Request, RequestStatus } from '@hcengineering/request'
import { Request, RequestDecisionComment, RequestStatus } from '@hcengineering/request'
import request from './plugin'
import view from '@hcengineering/model-view'
import { TComment } from '@hcengineering/model-chunter'
export const DOMAIN_REQUEST = 'request' as Domain
@ -45,12 +58,19 @@ export class TRequest extends TAttachedDoc implements Request {
tx!: TxCUD<Doc>
@Prop(TypeRef(contact.class.EmployeeAccount), request.string.Rejected)
@ReadOnly()
rejected?: Ref<EmployeeAccount>
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
comments?: number
}
@Mixin(request.mixin.RequestDecisionComment, chunter.class.Comment)
export class TRequestDecisionComment extends TComment implements RequestDecisionComment {}
export function createModel (builder: Builder): void {
builder.createModel(TRequest)
builder.createModel(TRequest, TRequestDecisionComment)
builder.mixin(request.class.Request, core.class.Class, view.mixin.ObjectEditor, {
editor: request.component.EditRequest

View File

@ -0,0 +1,31 @@
<script lang="ts">
export let value: any
</script>
<div class="container" class:yes={value === true} class:no={value === false}>
<svg class="svg-small" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<circle class:yes={value === true} class:no={value === false} cx="8" cy="8" r="6" />
{#if value === true}
<polygon fill="#fff" points="7.4,10.9 4.9,8.4 5.7,7.6 7.3,9.1 10.2,5.6 11.1,6.4 " />
{:else if value === false}
<polygon fill="#fff" points="10.7,6 10,5.3 8,7.3 6,5.3 5.3,6 7.3,8 5.3,10 6,10.7 8,8.7 10,10.7 10.7,10 8.7,8 " />
{:else}
<path
fill="#fff"
d="M7.3,9.3h1.3V9c0.1-0.5,0.6-0.9,1.1-1.4c0.4-0.4,0.8-0.9,0.8-1.6c0-1.1-0.8-1.8-2.2-1.8c-1.4,0-2.4,0.8-2.5,2.2 h1.4c0.1-0.6,0.4-1,1-1C8.8,5.4,9,5.7,9,6.2c0,0.4-0.3,0.7-0.7,1.1c-0.5,0.5-1,0.9-1,1.7V9.3z M8,11.6c0.5,0,0.9-0.4,0.9-0.9 c0-0.5-0.4-0.9-0.9-0.9c-0.5,0-0.9,0.4-0.9,0.9C7.1,11.2,7.5,11.6,8,11.6z"
/>
{/if}
</svg>
</div>
<style lang="scss">
.container {
fill: #77818e;
&.yes {
fill: #77c07b;
}
&.no {
fill: #f96e50;
}
}
</style>

View File

@ -154,6 +154,7 @@ export { default as FocusHandler } from './components/FocusHandler.svelte'
export { default as ListView } from './components/ListView.svelte'
export { default as ToggleButton } from './components/ToggleButton.svelte'
export { default as ExpandCollapse } from './components/ExpandCollapse.svelte'
export { default as BooleanIcon } from './components/BooleanIcon.svelte'
export { default as Expandable } from './components/Expandable.svelte'
export { default as BarDashboard } from './components/BarDashboard.svelte'
export { default as Notifications } from './components/notifications/Notifications.svelte'

View File

@ -257,23 +257,17 @@
{#if hasMessageType}
<div class="time"><TimeSince value={tx.tx.modifiedOn} /></div>
{/if}
{#if isMessageType(m.attribute)}
<div class="strong message emphasized">
{#if value.isObjectSet}
<ObjectPresenter value={value.set} />
{:else}
<svelte:component this={m.presenter} value={value.set} />
{/if}
</div>
{:else}
<div class="strong">
{#if value.isObjectSet}
<ObjectPresenter value={value.set} />
{:else}
<svelte:component this={m.presenter} value={value.set} />
{/if}
</div>
{/if}
<div
class="strong"
class:message={isMessageType(m.attribute)}
class:emphasized={isMessageType(m.attribute)}
>
{#if value.isObjectSet}
<ObjectPresenter value={value.set} />
{:else}
<svelte:component this={m.presenter} value={value.set} />
{/if}
</div>
{/if}
{/await}
{/each}
@ -290,23 +284,17 @@
<span class="lower"><Label label={m.label} /></span>
<Label label={activity.string.To} />
</span>
{#if isMessageType(m.attribute)}
<div class="strong message emphasized">
{#if value.isObjectSet}
<ObjectPresenter value={value.set} />
{:else}
<svelte:component this={m.presenter} value={value.set} />
{/if}
</div>
{:else}
<div class="strong">
{#if value.isObjectSet}
<ObjectPresenter value={value.set} />
{:else}
<svelte:component this={m.presenter} value={value.set} />
{/if}
</div>
{/if}
<div
class="strong"
class:message={isMessageType(m.attribute)}
class:emphasized={isMessageType(m.attribute)}
>
{#if value.isObjectSet}
<ObjectPresenter value={value.set} />
{:else}
<svelte:component this={m.presenter} value={value.set} />
{/if}
</div>
{/if}
{/await}
{/each}

View File

@ -16,7 +16,7 @@
<script lang="ts">
import { Employee, EmployeeAccount, formatName } from '@hcengineering/contact'
import { Account } from '@hcengineering/core'
import { Avatar, getClient } from '@hcengineering/presentation'
import { Avatar, createQuery } from '@hcengineering/presentation'
import { showPopup } from '@hcengineering/ui'
import { EditDoc } from '@hcengineering/view-resources'
import contact from '../plugin'
@ -30,12 +30,10 @@
showPopup(EditDoc, { _id: employee._id, _class: employee._class }, 'content')
}
}
const client = getClient()
const query = createQuery()
$: if (value._class === contact.class.EmployeeAccount) {
client.findOne(contact.class.Employee, { _id: (value as EmployeeAccount).employee }).then((r) => {
employee = r
})
query.query(contact.class.Employee, { _id: (value as EmployeeAccount).employee }, (r) => ([employee] = r))
}
</script>

View File

@ -0,0 +1,34 @@
<!--
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { EmployeeAccount } from '@hcengineering/contact'
import { Ref } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation'
import contact from '../plugin'
import EmployeeAccountPresenter from './EmployeeAccountPresenter.svelte'
export let value: Ref<EmployeeAccount>
let account: EmployeeAccount | undefined
const query = createQuery()
$: query.query(contact.class.EmployeeAccount, { _id: value }, (r) => ([account] = r))
</script>
{#if account}
<EmployeeAccountPresenter value={account} />
{/if}

View File

@ -36,6 +36,7 @@ import EditMember from './components/EditMember.svelte'
import EditOrganization from './components/EditOrganization.svelte'
import EditPerson from './components/EditPerson.svelte'
import EmployeeAccountPresenter from './components/EmployeeAccountPresenter.svelte'
import EmployeeAccountRefPresenter from './components/EmployeeAccountRefPresenter.svelte'
import EmployeeArrayEditor from './components/EmployeeArrayEditor.svelte'
import AccountArrayEditor from './components/AccountArrayEditor.svelte'
import EmployeeBrowser from './components/EmployeeBrowser.svelte'
@ -65,7 +66,8 @@ export {
OrganizationPresenter,
EmployeeBrowser,
MemberPresenter,
EmployeeEditor
EmployeeEditor,
EmployeeAccountRefPresenter
}
const toObjectSearchResult = (e: WithLookup<Contact>): ObjectSearchResult => ({

View File

@ -40,6 +40,7 @@
"@hcengineering/activity": "^0.6.0",
"@hcengineering/activity-resources": "^0.6.1",
"@hcengineering/contact": "^0.6.9",
"@hcengineering/contact-resources": "^0.6.0",
"@hcengineering/chunter": "^0.6.2",
"@hcengineering/chunter-resources": "^0.6.0",
"@hcengineering/attachment-resources": "^0.6.0",

View File

@ -19,7 +19,7 @@
import { createEventDispatcher, onMount } from 'svelte'
import request from '../plugin'
import RequestActions from './RequestActions.svelte'
import RequestLabel from './RequestLabel.svelte'
import RequestDetail from './RequestDetail.svelte'
import TxView from './TxView.svelte'
export let object: Request
@ -27,16 +27,19 @@
const dispatch = createEventDispatcher()
onMount(() => {
dispatch('open', { ignoreKeys: ['comments', 'status'], activityOptions: { enabled: true, showInput: false } })
dispatch('open', {
ignoreKeys: ['comments', 'status', 'rejected', 'approved', 'requested'],
activityOptions: { enabled: true, showInput: false }
})
})
</script>
{#if object !== undefined}
<div class="flex-row-center gap-1 mb-2">
<span class="mr-1"><Label label={request.string.For} /></span>
<ObjectPresenter objectId={object.tx.objectId} _class={object.tx.objectClass} />
<span class="mr-1"><ObjectPresenter objectId={object.tx.objectId} _class={object.tx.objectClass} /></span>
<TxView tx={object.tx} />
</div>
<RequestLabel value={object} />
<RequestDetail value={object} />
<RequestActions value={object} />
{/if}

View File

@ -16,34 +16,22 @@
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
import chunter, { Comment } from '@hcengineering/chunter'
import { updateBacklinks } from '@hcengineering/chunter-resources/src/backlinks'
import contact, { EmployeeAccount } from '@hcengineering/contact'
import { EmployeeAccount } from '@hcengineering/contact'
import { AttachedData, getCurrentAccount, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { getClient } from '@hcengineering/presentation'
import { Request, RequestStatus } from '@hcengineering/request'
import request from '../plugin'
import type { RefAction } from '@hcengineering/text-editor'
import request from '../plugin'
import Comments from './icons/Comments.svelte'
import DocFail from './icons/DocFail.svelte'
import DocSuccess from './icons/DocSuccess.svelte'
import Comments from './icons/Comments.svelte'
export let value: Request
let employee: EmployeeAccount | undefined
const query = createQuery()
const client = getClient()
const me = getCurrentAccount()._id as Ref<EmployeeAccount>
$: query.query(
contact.class.EmployeeAccount,
{ _id: value.tx.modifiedBy as Ref<EmployeeAccount> },
(account) => {
;[employee] = account
},
{ limit: 1 }
)
const approvable = value.requested.includes(me) && !value.approved.includes(me)
const approvable = value.requested.filter((a) => a === me).length > value.approved.filter((a) => a === me).length
async function approve () {
await saveComment()
@ -59,6 +47,7 @@
async function reject () {
await saveComment()
await client.update(value, {
rejected: me,
status: RequestStatus.Rejected
})
}
@ -71,12 +60,14 @@
attachments = event.detail.attachments
}
async function saveComment () {
await client.addCollection(chunter.class.Comment, value.space, value._id, value._class, 'comments', {
async function saveComment (): Promise<void> {
const _id = await client.addCollection(chunter.class.Comment, value.space, value._id, value._class, 'comments', {
message,
attachments
})
await client.createMixin(_id, chunter.class.Comment, value.space, request.mixin.RequestDecisionComment, {})
// We need to update backlinks before and after.
await updateBacklinks(client, value.attachedTo, value.attachedToClass, value._id, message)
refInput.submit()

View File

@ -0,0 +1,93 @@
<!--
// Copyright © 2022 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import contact, { EmployeeAccount } from '@hcengineering/contact'
import { EmployeeAccountRefPresenter } from '@hcengineering/contact-resources'
import { Account, Ref } from '@hcengineering/core'
import { createQuery, MessageViewer } from '@hcengineering/presentation'
import { Request, RequestDecisionComment } from '@hcengineering/request'
import { BooleanIcon, Label, ShowMore } from '@hcengineering/ui'
import request from '../plugin'
export let value: Request
let comments: Map<Ref<Account>, RequestDecisionComment> = new Map()
const query = createQuery()
$: query.query(
request.mixin.RequestDecisionComment,
{ attachedTo: value._id },
(res) => (comments = new Map(res.map((r) => [r.modifiedBy, r])))
)
interface RequestDecision {
employee: Ref<EmployeeAccount>
decision?: boolean
comment?: RequestDecisionComment
}
function convert (value: Request, comments: Map<Ref<Account>, RequestDecisionComment>): RequestDecision[] {
const res: RequestDecision[] = []
for (const emp of value.requested) {
const decision = value.rejected === emp ? false : value.approved.includes(emp) ? true : undefined
const result: RequestDecision = {
employee: emp,
decision
}
if (decision !== undefined) {
result.comment = comments.get(emp)
}
res.push(result)
}
return res
}
</script>
<table class="antiTable">
<thead class="scroller-thead">
<tr class="scroller-thead__tr">
<th><Label label={contact.string.Employee} /></th>
<th><Label label={request.string.Approved} /></th>
<th><Label label={request.string.Comment} /></th>
</tr>
</thead>
<tbody>
{#each convert(value, comments) as requested}
<tr class="antiTable-body__row">
<td><EmployeeAccountRefPresenter value={requested.employee} /></td>
<td><BooleanIcon value={requested.decision} /></td>
<td
>{#if requested.comment}
<ShowMore limit={126} fixed>
<MessageViewer message={requested.comment.message} />
</ShowMore>{/if}</td
>
</tr>
{/each}
</tbody>
</table>
<style lang="scss">
.antiTable {
th,
td {
&:first-child {
padding-left: 1.5rem;
}
&:last-child {
padding-right: 1.5rem;
}
}
}
</style>

View File

@ -0,0 +1,24 @@
<!--
// Copyright © 2022 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { Request } from '@hcengineering/request'
import RequestDetail from './RequestDetail.svelte'
export let value: Request
</script>
<div class="antiPopup popup p-4">
<RequestDetail {value} />
</div>

View File

@ -14,17 +14,31 @@
-->
<script lang="ts">
import { Request, RequestStatus } from '@hcengineering/request'
import { Label } from '@hcengineering/ui'
import { Button, eventToHTMLElement, ProgressCircle, showPopup } from '@hcengineering/ui'
import RequestStatusPresenter from './RequestStatusPresenter.svelte'
import request from '../plugin'
import RequestDetailPopup from './RequestDetailPopup.svelte'
export let value: Request
</script>
<div class="flex gap-2">
{#if value.status !== RequestStatus.Active}
<RequestStatusPresenter value={value.status === RequestStatus.Completed} />
{:else}
<Label label={request.string.Approved} />: {value.approved.length}/{value.requiredApprovesCount}
{/if}
<Button
on:click={(ev) => {
ev.stopPropagation()
showPopup(RequestDetailPopup, { value }, eventToHTMLElement(ev))
}}
>
<svelte:fragment slot="content">
{#if value.status !== RequestStatus.Active}
<RequestStatusPresenter value={value.status === RequestStatus.Completed} />
{:else}
<div class="flex-row-center content-color text-sm pointer-events-none">
<div class="mr-1">
<ProgressCircle max={value.requiredApprovesCount} value={value.approved.length} size={'inline'} primary />
</div>
{value.approved.length}/{value.requiredApprovesCount}
</div>
{/if}
</svelte:fragment>
</Button>
</div>

View File

@ -26,9 +26,6 @@
const client = getClient()
$: cl = client.getHierarchy().getClass(value._class)
$: label = cl.shortLabel
let accounts: WithLookup<EmployeeAccount>[] = []
$: client.findAll(contact.class.EmployeeAccount, { _id: { $in: value.requested } }).then((res) => {
@ -39,7 +36,7 @@
<div class="flex">
<a
class="flex-presenter"
class="flex-presenter mr-1"
class:inline-presenter={inline}
href="#{getPanelURI(view.component.EditDoc, value._id, value._class, 'content')}"
>

View File

@ -14,7 +14,7 @@
-->
<script lang="ts">
import { IntlString } from '@hcengineering/platform'
import { Label } from '@hcengineering/ui'
import { BooleanIcon, Label } from '@hcengineering/ui'
import request from '../plugin'
export let value: boolean
@ -31,19 +31,7 @@
class:no={value === false}
class:unknown={value === undefined}
>
<svg class="svg-small" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<circle class:yes={value === true} class:no={value === false} cx="8" cy="8" r="6" />
{#if value === true}
<polygon fill="#fff" points="7.4,10.9 4.9,8.4 5.7,7.6 7.3,9.1 10.2,5.6 11.1,6.4 " />
{:else if value === false}
<polygon fill="#fff" points="10.7,6 10,5.3 8,7.3 6,5.3 5.3,6 7.3,8 5.3,10 6,10.7 8,8.7 10,10.7 10.7,10 8.7,8 " />
{:else}
<path
fill="#fff"
d="M7.3,9.3h1.3V9c0.1-0.5,0.6-0.9,1.1-1.4c0.4-0.4,0.8-0.9,0.8-1.6c0-1.1-0.8-1.8-2.2-1.8c-1.4,0-2.4,0.8-2.5,2.2 h1.4c0.1-0.6,0.4-1,1-1C8.8,5.4,9,5.7,9,6.2c0,0.4-0.3,0.7-0.7,1.1c-0.5,0.5-1,0.9-1,1.7V9.3z M8,11.6c0.5,0,0.9-0.4,0.9-0.9 c0-0.5-0.4-0.9-0.9-0.9c-0.5,0-0.9,0.4-0.9,0.9C7.1,11.2,7.5,11.6,8,11.6z"
/>
{/if}
</svg>
<BooleanIcon {value} />
<span><Label label={getBooleanLabel(value)} /></span>
</div>
@ -52,14 +40,6 @@
max-width: fit-content;
user-select: none;
fill: #77818e;
&.yes {
fill: #77c07b;
}
&.no {
fill: #f96e50;
}
span {
margin-left: 0.25rem;
white-space: nowrap;

View File

@ -30,7 +30,10 @@
requested: me,
status: RequestStatus.Active
},
(res) => (requests = res.filter((p) => !p.approved.includes(me)))
(res) =>
(requests = res.filter(
(p) => p.requested.filter((a) => a === me).length > p.approved.filter((a) => a === me).length
))
)
</script>

View File

@ -139,7 +139,7 @@
<Label label={activity.string.To} />
</span>
<div
class="strong message emphasized"
class="strong"
class:message={isMessageType(m.attribute)}
class:emphasized={isMessageType(m.attribute)}
>
@ -148,7 +148,6 @@
{:else}
<svelte:component this={m.presenter} value={value.set} />
{/if}
<svelte:component this={m.presenter} value={value.set} />
</div>
{/if}
{/await}
@ -166,15 +165,13 @@
<span class="lower"><Label label={m.label} /></span>
<Label label={activity.string.To} />
</span>
{#if isMessageType(m.attribute)}
<div class="strong message emphasized">
<svelte:component this={m.presenter} value={value.set} />
</div>
{:else}
<div class="strong">
<svelte:component this={m.presenter} value={value.set} />
</div>
{/if}
<div
class="strong"
class:message={isMessageType(m.attribute)}
class:emphasized={isMessageType(m.attribute)}
>
<svelte:component this={m.presenter} value={value.set} />
</div>
{/if}
{/await}
{/each}

View File

@ -25,7 +25,7 @@
</script>
{#if request}
<div class="mr-2">
<div class="ml-1">
<RequestLabel value={request} />
</div>
{/if}

View File

@ -28,6 +28,7 @@
"dependencies": {
"@hcengineering/platform": "^0.6.8",
"@hcengineering/core": "^0.6.20",
"@hcengineering/chunter": "^0.6.2",
"@hcengineering/ui": "^0.6.3",
"@hcengineering/contact": "^0.6.9"
}

View File

@ -14,10 +14,11 @@
//
import { EmployeeAccount } from '@hcengineering/contact'
import type { AttachedDoc, Class, Doc, Ref, TxCUD } from '@hcengineering/core'
import type { AttachedDoc, Class, Doc, Mixin, Ref, TxCUD } from '@hcengineering/core'
import type { Asset, IntlString, Plugin } from '@hcengineering/platform'
import { plugin } from '@hcengineering/platform'
import { AnyComponent } from '@hcengineering/ui'
import { Comment } from '@hcengineering/chunter'
/**
* @public
@ -26,11 +27,17 @@ export interface Request extends AttachedDoc {
requested: Ref<EmployeeAccount>[]
approved: Ref<EmployeeAccount>[]
requiredApprovesCount: number
rejected?: Ref<EmployeeAccount>
status: RequestStatus
tx: TxCUD<Doc>
comments?: number
}
/**
* @public
*/
export interface RequestDecisionComment extends Comment {}
/**
* @public
*/
@ -52,6 +59,9 @@ const request = plugin(requestId, {
class: {
Request: '' as Ref<Class<Request>>
},
mixin: {
RequestDecisionComment: '' as Ref<Mixin<RequestDecisionComment>>
},
component: {
RequestsPopup: '' as AnyComponent
},

View File

@ -14,31 +14,14 @@
// limitations under the License.
-->
<script lang="ts">
import { Label } from '@hcengineering/ui'
import { BooleanIcon, Label } from '@hcengineering/ui'
import { getBooleanLabel } from '../utils'
export let value: any
</script>
<div
class="flex-row-center yesno-container"
class:yes={value === true}
class:no={value === false}
class:unknown={value === undefined}
>
<svg class="svg-small" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<circle class:yes={value === true} class:no={value === false} cx="8" cy="8" r="6" />
{#if value === true}
<polygon fill="#fff" points="7.4,10.9 4.9,8.4 5.7,7.6 7.3,9.1 10.2,5.6 11.1,6.4 " />
{:else if value === false}
<polygon fill="#fff" points="10.7,6 10,5.3 8,7.3 6,5.3 5.3,6 7.3,8 5.3,10 6,10.7 8,8.7 10,10.7 10.7,10 8.7,8 " />
{:else}
<path
fill="#fff"
d="M7.3,9.3h1.3V9c0.1-0.5,0.6-0.9,1.1-1.4c0.4-0.4,0.8-0.9,0.8-1.6c0-1.1-0.8-1.8-2.2-1.8c-1.4,0-2.4,0.8-2.5,2.2 h1.4c0.1-0.6,0.4-1,1-1C8.8,5.4,9,5.7,9,6.2c0,0.4-0.3,0.7-0.7,1.1c-0.5,0.5-1,0.9-1,1.7V9.3z M8,11.6c0.5,0,0.9-0.4,0.9-0.9 c0-0.5-0.4-0.9-0.9-0.9c-0.5,0-0.9,0.4-0.9,0.9C7.1,11.2,7.5,11.6,8,11.6z"
/>
{/if}
</svg>
<div class="flex-row-center yesno-container">
<BooleanIcon {value} />
<span><Label label={getBooleanLabel(value)} /></span>
</div>
@ -47,14 +30,6 @@
max-width: fit-content;
user-select: none;
fill: #77818e;
&.yes {
fill: #77c07b;
}
&.no {
fill: #f96e50;
}
span {
margin-left: 0.25rem;
width: 1.6rem;

View File

@ -152,7 +152,12 @@
requested: account._id,
status: RequestStatus.Active
},
(res) => (hasRequests = res.filter((p) => !p.approved.includes(account._id)).length > 0)
(res) =>
(hasRequests =
res.filter(
(p) =>
p.requested.filter((a) => a === account._id).length > p.approved.filter((a) => a === account._id).length
).length > 0)
)
onDestroy(