mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-23 08:48:01 +00:00
UBER-496: Fix few issues (#3439)
This commit is contained in:
parent
354e76e1f1
commit
05d273bb59
5
.vscode/launch.json
vendored
5
.vscode/launch.json
vendored
@ -69,7 +69,10 @@
|
||||
"TRANSACTOR_URL": "ws:/localhost:3333",
|
||||
"ACCOUNT_PORT": "3000",
|
||||
"FRONT_URL": "http://localhost:8080",
|
||||
"SES_URL": "http://localhost:8091"
|
||||
"SES_URL": "http://localhost:8091",
|
||||
"MINIO_ACCESS_KEY": "minioadmin",
|
||||
"MINIO_SECRET_KEY": "minioadmin",
|
||||
"MINIO_ENDPOINT": "localhost"
|
||||
},
|
||||
"runtimeArgs": ["--nolazy", "-r", "ts-node/register"],
|
||||
"sourceMaps": true,
|
||||
|
@ -627,6 +627,9 @@ export function createModel (builder: Builder): void {
|
||||
builder.mixin(core.class.Account, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: contact.component.EmployeeAccountPresenter
|
||||
})
|
||||
builder.mixin(core.class.Account, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: contact.component.EmployeeAccountRefPresenter
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Organization, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: contact.component.OrganizationPresenter
|
||||
|
@ -36,6 +36,7 @@ export default mergeIds(contactId, contact, {
|
||||
Contacts: '' as AnyComponent,
|
||||
ContactsTabs: '' as AnyComponent,
|
||||
EmployeeAccountPresenter: '' as AnyComponent,
|
||||
EmployeeAccountRefPresenter: '' as AnyComponent,
|
||||
OrganizationEditor: '' as AnyComponent,
|
||||
EmployeePresenter: '' as AnyComponent,
|
||||
EmployeeRefPresenter: '' as AnyComponent,
|
||||
|
@ -651,7 +651,7 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
const applicantViewOptions = (colors: boolean): ViewOptionsModel => {
|
||||
const model: ViewOptionsModel = {
|
||||
groupBy: ['state', 'assignee', 'space'],
|
||||
groupBy: ['state', 'assignee', 'space', 'createdBy', 'modifiedBy'],
|
||||
orderBy: [
|
||||
['state', SortingOrder.Ascending],
|
||||
['modifiedOn', SortingOrder.Descending],
|
||||
|
@ -70,7 +70,7 @@ import {
|
||||
TimeSpendReport,
|
||||
trackerId
|
||||
} from '@hcengineering/tracker'
|
||||
import { KeyBinding, ViewOptionsModel } from '@hcengineering/view'
|
||||
import { KeyBinding, ViewAction, ViewOptionsModel } from '@hcengineering/view'
|
||||
import tracker from './plugin'
|
||||
|
||||
import { generateClassNotificationTypes } from '@hcengineering/model-notification'
|
||||
@ -385,7 +385,7 @@ export function createModel (builder: Builder): void {
|
||||
)
|
||||
|
||||
const issuesOptions = (kanban: boolean): ViewOptionsModel => ({
|
||||
groupBy: ['status', 'assignee', 'priority', 'component', 'milestone'],
|
||||
groupBy: ['status', 'assignee', 'priority', 'component', 'milestone', 'createdBy', 'modifiedBy'],
|
||||
orderBy: [
|
||||
['status', SortingOrder.Ascending],
|
||||
['priority', SortingOrder.Descending],
|
||||
@ -554,7 +554,7 @@ export function createModel (builder: Builder): void {
|
||||
)
|
||||
|
||||
const subIssuesOptions: ViewOptionsModel = {
|
||||
groupBy: ['status', 'assignee', 'priority', 'milestone'],
|
||||
groupBy: ['status', 'assignee', 'priority', 'milestone', 'createdBy', 'modifiedBy'],
|
||||
orderBy: [
|
||||
['rank', SortingOrder.Ascending],
|
||||
['status', SortingOrder.Ascending],
|
||||
@ -667,7 +667,7 @@ export function createModel (builder: Builder): void {
|
||||
attachTo: tracker.class.IssueTemplate,
|
||||
descriptor: view.viewlet.List,
|
||||
viewOptions: {
|
||||
groupBy: ['assignee', 'priority', 'component', 'milestone'],
|
||||
groupBy: ['assignee', 'priority', 'component', 'milestone', 'createdBy', 'modifiedBy'],
|
||||
orderBy: [
|
||||
['priority', SortingOrder.Ascending],
|
||||
['modifiedOn', SortingOrder.Descending],
|
||||
@ -1060,6 +1060,20 @@ export function createModel (builder: Builder): void {
|
||||
['backlog', tracker.string.Backlog, {}]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'all-projects',
|
||||
component: workbench.component.SpecialView,
|
||||
icon: view.icon.Archive,
|
||||
label: tracker.string.AllProjects,
|
||||
position: 'bottom',
|
||||
visibleIf: workbench.function.IsOwner,
|
||||
spaceClass: tracker.class.Project,
|
||||
componentProps: {
|
||||
_class: tracker.class.Project,
|
||||
label: tracker.string.AllIssues,
|
||||
icon: tracker.icon.Issues
|
||||
}
|
||||
}
|
||||
],
|
||||
spaces: [
|
||||
@ -1068,6 +1082,7 @@ export function createModel (builder: Builder): void {
|
||||
spaceClass: tracker.class.Project,
|
||||
addSpaceLabel: tracker.string.CreateProject,
|
||||
createComponent: tracker.component.CreateProject,
|
||||
visibleIf: tracker.function.IsProjectJoined,
|
||||
icon: tracker.icon.Home,
|
||||
specials: [
|
||||
{
|
||||
@ -1162,9 +1177,7 @@ export function createModel (builder: Builder): void {
|
||||
input: 'focus',
|
||||
category: tracker.category.Tracker,
|
||||
target: tracker.class.Project,
|
||||
query: {
|
||||
archived: false
|
||||
},
|
||||
query: {},
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'edit'
|
||||
@ -1182,9 +1195,7 @@ export function createModel (builder: Builder): void {
|
||||
input: 'focus',
|
||||
category: tracker.category.Tracker,
|
||||
target: tracker.class.Project,
|
||||
query: {
|
||||
archived: false
|
||||
},
|
||||
query: {},
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'edit'
|
||||
@ -1213,6 +1224,28 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
tracker.action.DeleteProject
|
||||
)
|
||||
createAction(builder, {
|
||||
label: tracker.string.Unarchive,
|
||||
icon: view.icon.Archive,
|
||||
action: view.actionImpl.UpdateDocument as ViewAction,
|
||||
actionProps: {
|
||||
key: 'archived',
|
||||
ask: true,
|
||||
value: false,
|
||||
label: tracker.string.Unarchive,
|
||||
message: tracker.string.UnarchiveConfirm
|
||||
},
|
||||
input: 'any',
|
||||
category: tracker.category.Tracker,
|
||||
query: {
|
||||
archived: true
|
||||
},
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'tools'
|
||||
},
|
||||
target: tracker.class.Project
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
@ -1391,6 +1424,10 @@ export function createModel (builder: Builder): void {
|
||||
override: [view.action.Open]
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Project, core.class.Class, view.mixin.IgnoreActions, {
|
||||
actions: [view.action.Open]
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Issue, core.class.Class, view.mixin.ClassFilters, {
|
||||
filters: [
|
||||
'status',
|
||||
@ -1852,7 +1889,7 @@ export function createModel (builder: Builder): void {
|
||||
)
|
||||
|
||||
const milestoneOptions: ViewOptionsModel = {
|
||||
groupBy: ['status'],
|
||||
groupBy: ['status', 'createdBy', 'modifiedBy'],
|
||||
orderBy: [
|
||||
['modifiedOn', SortingOrder.Descending],
|
||||
['targetDate', SortingOrder.Descending],
|
||||
@ -1934,7 +1971,7 @@ export function createModel (builder: Builder): void {
|
||||
)
|
||||
|
||||
const componentListViewOptions: ViewOptionsModel = {
|
||||
groupBy: ['lead'],
|
||||
groupBy: ['lead', 'createdBy', 'modifiedBy'],
|
||||
orderBy: [
|
||||
['modifiedOn', SortingOrder.Descending],
|
||||
['createdOn', SortingOrder.Descending]
|
||||
@ -1973,4 +2010,33 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
tracker.viewlet.ComponentList
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
attachTo: tracker.class.Project,
|
||||
descriptor: view.viewlet.List,
|
||||
viewOptions: {
|
||||
groupBy: ['createdBy'],
|
||||
orderBy: [
|
||||
['modifiedOn', SortingOrder.Descending],
|
||||
['createdOn', SortingOrder.Descending]
|
||||
],
|
||||
other: [showColorsViewOption]
|
||||
},
|
||||
configOptions: {
|
||||
strict: true,
|
||||
hiddenKeys: ['label', 'description']
|
||||
},
|
||||
config: [
|
||||
{
|
||||
key: '',
|
||||
props: { kind: 'list' }
|
||||
},
|
||||
{ key: '', displayProps: { grow: true } }
|
||||
]
|
||||
},
|
||||
tracker.viewlet.ProjectList
|
||||
)
|
||||
}
|
||||
|
@ -40,7 +40,10 @@ export default mergeIds(trackerId, tracker, {
|
||||
CreatedDate: '' as IntlString,
|
||||
ChangeStatus: '' as IntlString,
|
||||
ConfigLabel: '' as IntlString,
|
||||
ConfigDescription: '' as IntlString
|
||||
ConfigDescription: '' as IntlString,
|
||||
Unarchive: '' as IntlString,
|
||||
UnarchiveConfirm: '' as IntlString,
|
||||
AllProjects: '' as IntlString
|
||||
},
|
||||
activity: {
|
||||
TxIssueCreated: '' as AnyComponent,
|
||||
@ -62,7 +65,8 @@ export default mergeIds(trackerId, tracker, {
|
||||
IssueTemplateList: '' as Ref<Viewlet>,
|
||||
IssueKanban: '' as Ref<Viewlet>,
|
||||
MilestoneList: '' as Ref<Viewlet>,
|
||||
ComponentList: '' as Ref<Viewlet>
|
||||
ComponentList: '' as Ref<Viewlet>,
|
||||
ProjectList: '' as Ref<Viewlet>
|
||||
},
|
||||
ids: {
|
||||
TxIssueCreated: '' as Ref<TxViewlet>,
|
||||
|
@ -13,8 +13,8 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Space } from '@hcengineering/core'
|
||||
import { IntlString, mergeIds, Resource } from '@hcengineering/platform'
|
||||
import { Doc, Space } from '@hcengineering/core'
|
||||
import { IntlString, Resource, mergeIds } from '@hcengineering/platform'
|
||||
import { AnyComponent } from '@hcengineering/ui'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
import workbench from '@hcengineering/workbench-resources/src/plugin'
|
||||
@ -32,6 +32,7 @@ export default mergeIds(workbenchId, workbench, {
|
||||
HiddenApplication: '' as IntlString
|
||||
},
|
||||
function: {
|
||||
HasArchiveSpaces: '' as Resource<(spaces: Space[]) => Promise<boolean>>
|
||||
HasArchiveSpaces: '' as Resource<(spaces: Space[]) => Promise<boolean>>,
|
||||
IsOwner: '' as Resource<(docs: Doc[]) => Promise<boolean>>
|
||||
}
|
||||
})
|
||||
|
@ -7,6 +7,18 @@
|
||||
"heftEvent": "clean",
|
||||
"actionId": "defaultClean",
|
||||
"globsToDelete": ["dist", "lib", "temp"]
|
||||
},
|
||||
{
|
||||
"actionKind": "copyFiles",
|
||||
"heftEvent": "pre-compile",
|
||||
"actionId": "copy-lang",
|
||||
"copyOperations": [
|
||||
{
|
||||
"sourceFolder": "src",
|
||||
"destinationFolders": ["lib"],
|
||||
"includeGlobs": ["lang"]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"heftPlugins": [
|
||||
|
@ -7,6 +7,18 @@
|
||||
"heftEvent": "clean",
|
||||
"actionId": "defaultClean",
|
||||
"globsToDelete": ["dist", "lib", "temp"]
|
||||
},
|
||||
{
|
||||
"actionKind": "copyFiles",
|
||||
"heftEvent": "pre-compile",
|
||||
"actionId": "copy-lang",
|
||||
"copyOperations": [
|
||||
{
|
||||
"sourceFolder": "src",
|
||||
"destinationFolders": ["lib"],
|
||||
"includeGlobs": ["lang"]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"heftPlugins": [
|
||||
|
@ -14,6 +14,7 @@
|
||||
"WorkspaceNotFound": "Workspace not found",
|
||||
"InvalidPassword": "Invalid password",
|
||||
"AccountAlreadyExists": "Account already exists",
|
||||
"WorkspaceRateLimit": "Server is busy, Please wait a bit and try again",
|
||||
"AccountAlreadyConfirmed": "Account already confirmed",
|
||||
"AccountWasMerged": "Account was merged",
|
||||
"WorkspaceAlreadyExists": "Workspace already exists",
|
||||
|
@ -14,6 +14,7 @@
|
||||
"WorkspaceNotFound": "Рабочее пространство не найдено",
|
||||
"InvalidPassword": "Неверный пароль",
|
||||
"AccountAlreadyExists": "Аккаунт уже существует",
|
||||
"WorkspaceRateLimit": "Сервер перегружен, Пожалуйста подождите",
|
||||
"AccountAlreadyConfirmed": "Аккаунт уже подтвержден",
|
||||
"AccountWasMerged": "Аккаунт был объединен",
|
||||
"WorkspaceAlreadyExists": "Рабочее пространство уже существует",
|
||||
|
@ -152,6 +152,7 @@ export default plugin(platformId, {
|
||||
AccountAlreadyConfirmed: '' as StatusCode<{ account: string }>,
|
||||
AccountWasMerged: '' as StatusCode<{ account: string }>,
|
||||
WorkspaceAlreadyExists: '' as StatusCode<{ workspace: string }>,
|
||||
WorkspaceRateLimit: '' as StatusCode<{ workspace: string }>,
|
||||
ProductIdMismatch: '' as StatusCode<{ productId: string }>
|
||||
},
|
||||
metadata: {
|
||||
|
@ -36,7 +36,7 @@
|
||||
{#if value}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
{#if employee}
|
||||
<EmployeePresenter value={employee} {disabled} {inline} {accent} {avatarSize} />
|
||||
<EmployeePresenter value={employee} {disabled} {inline} {accent} {avatarSize} on:accent-color />
|
||||
{:else}
|
||||
<div class="flex-row-center">
|
||||
<Avatar size={avatarSize} />
|
||||
|
@ -30,5 +30,5 @@
|
||||
</script>
|
||||
|
||||
{#if account}
|
||||
<EmployeeAccountPresenter value={account} {disabled} {inline} {avatarSize} />
|
||||
<EmployeeAccountPresenter value={account} {disabled} {inline} {avatarSize} on:accent-color />
|
||||
{/if}
|
||||
|
@ -68,6 +68,7 @@ import EmployeePresenter from './components/EmployeePresenter.svelte'
|
||||
import EmployeeRefPresenter from './components/EmployeeRefPresenter.svelte'
|
||||
import MemberPresenter from './components/MemberPresenter.svelte'
|
||||
import Members from './components/Members.svelte'
|
||||
import MembersBox from './components/MembersBox.svelte'
|
||||
import MembersPresenter from './components/MembersPresenter.svelte'
|
||||
import MergeEmployee from './components/MergeEmployee.svelte'
|
||||
import OrganizationEditor from './components/OrganizationEditor.svelte'
|
||||
@ -141,7 +142,8 @@ export {
|
||||
UserInfo,
|
||||
IconMembers,
|
||||
SelectAvatars,
|
||||
UserBoxItems
|
||||
UserBoxItems,
|
||||
MembersBox
|
||||
}
|
||||
|
||||
const toObjectSearchResult = (e: WithLookup<Contact>): ObjectSearchResult => ({
|
||||
@ -311,7 +313,8 @@ export default async (): Promise<Resources> => ({
|
||||
EmployeeFilter,
|
||||
EmployeeFilterValuePresenter,
|
||||
EmployeeAccountFilterValuePresenter,
|
||||
DeleteConfirmationPopup
|
||||
DeleteConfirmationPopup,
|
||||
EmployeeAccountRefPresenter
|
||||
},
|
||||
completion: {
|
||||
EmployeeQuery: async (
|
||||
|
@ -18,7 +18,8 @@
|
||||
import type { Ref } from '@hcengineering/core'
|
||||
import core from '@hcengineering/core'
|
||||
import { Panel } from '@hcengineering/panel'
|
||||
import { createQuery, getClient, MembersBox } from '@hcengineering/presentation'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { MembersBox } from '@hcengineering/contact-resources'
|
||||
import type { Funnel } from '@hcengineering/lead'
|
||||
import { FullDescriptionBox } from '@hcengineering/text-editor'
|
||||
import { EditBox, Grid } from '@hcengineering/ui'
|
||||
|
@ -49,7 +49,7 @@
|
||||
$: queries = { assigned, created, subscribed }
|
||||
$: mode = $resolvedLocationStore.query?.mode ?? undefined
|
||||
|
||||
let searchQuery: DocumentQuery<Lead> = { ...baseQuery }
|
||||
let searchQuery: DocumentQuery<Lead> = { ...(baseQuery ?? {}) }
|
||||
function updateSearchQuery (search: string): void {
|
||||
searchQuery = search === '' ? { ...baseQuery } : { ...baseQuery, $search: search }
|
||||
}
|
||||
@ -72,11 +72,11 @@
|
||||
)
|
||||
}
|
||||
$: if (mode === 'subscribed') getSubscribed()
|
||||
$: if (mode === undefined || queries[mode] === undefined) {
|
||||
$: if (mode === undefined || (queries as any)[mode] === undefined) {
|
||||
;[[mode]] = config
|
||||
}
|
||||
$: if (mode !== undefined) {
|
||||
baseQuery = { ...queries[mode] }
|
||||
baseQuery = { ...((queries as any)[mode] ?? {}) }
|
||||
modeSelectorProps = {
|
||||
config,
|
||||
mode,
|
||||
@ -101,6 +101,9 @@
|
||||
.findOne<Viewlet>(view.class.Viewlet, { attachTo: _class, descriptor: task.viewlet.StatusTable })
|
||||
.then((res) => {
|
||||
viewlet = res
|
||||
if (res == null) {
|
||||
return
|
||||
}
|
||||
preferenceQuery.query(
|
||||
view.class.ViewletPreference,
|
||||
{
|
||||
|
@ -27,7 +27,7 @@
|
||||
"WantAnotherWorkspace": "Want to create another workspace?",
|
||||
"ChangeAccount": "Change account",
|
||||
"NotSeeingWorkspace": "Not seeing your workspace?",
|
||||
"WorkspaceNameRule": "The workspace name can contain lowercase letters, numbers and symbol -",
|
||||
"WorkspaceNameRule": "The workspace name can contain lowercase letters, numbers and symbol - (inside name)",
|
||||
"ForgotPassword": "Forgot your password?",
|
||||
"KnowPassword": "Know your password?",
|
||||
"Recover": "Recover",
|
||||
|
@ -27,7 +27,7 @@
|
||||
"WantAnotherWorkspace": "Хотите создать другое рабочее пространство?",
|
||||
"ChangeAccount": "Сменить пользователя",
|
||||
"NotSeeingWorkspace": "Не видите ваше рабочее пространство?",
|
||||
"WorkspaceNameRule": "Название рабочего пространства должно состояить из строчных латинских букв, цифр и символа -",
|
||||
"WorkspaceNameRule": "Название рабочего пространства должно состояить из строчных латинских букв, цифр и символа - (внутри имени)",
|
||||
"ForgotPassword": "Забыли пароль?",
|
||||
"KnowPassword": "Знаете пароль?",
|
||||
"Recover": "Восстановить",
|
||||
|
@ -28,7 +28,7 @@
|
||||
{
|
||||
name: 'workspace',
|
||||
i18n: login.string.Workspace,
|
||||
rule: /^[0-9a-z\-)(]{3,63}$/,
|
||||
rule: /^[0-9a-z][0-9a-z-]{2,62}[0-9a-z]$/,
|
||||
ruleDescr: login.string.WorkspaceNameRule
|
||||
}
|
||||
]
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
|
||||
import chunter, { Comment } from '@hcengineering/chunter'
|
||||
import { updateBacklinks } from '@hcengineering/chunter-resources/src/backlinks'
|
||||
import { updateBacklinks } from '@hcengineering/chunter-resources'
|
||||
import { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { AttachedData, getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
|
@ -15,6 +15,6 @@
|
||||
"Field": "Поле",
|
||||
"TemplateCategory": "Группа шаблонов",
|
||||
"CreateTemplateCategory": "Создать группу",
|
||||
"Copy": "Копировать"
|
||||
"Copy": "Копировать"
|
||||
}
|
||||
}
|
@ -273,7 +273,10 @@
|
||||
|
||||
"NoStatusFound": "No matching status found",
|
||||
"CreateMissingStatus": "Create missing status",
|
||||
"UnsetParent": "Parent issue will be unset"
|
||||
"UnsetParent": "Parent issue will be unset",
|
||||
"Unarchive": "Unarchive",
|
||||
"UnarchiveConfirm": "Do you want to unarchive project?",
|
||||
"AllProjects": "All projects"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
|
@ -272,7 +272,10 @@
|
||||
"ProjectEmojiiCategory": "Эмодзи",
|
||||
"NoStatusFound": "Статус не найдет",
|
||||
"CreateMissingStatus": "Создать отсутствущий статус",
|
||||
"UnsetParent": "Родительская задача будет убрана"
|
||||
"UnsetParent": "Родительская задача будет убрана",
|
||||
"Unarchive": "Разархивировать",
|
||||
"UnarchiveConfirm": "Вы действительно хотите разархивировать?",
|
||||
"AllProjects": "All projects"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
|
@ -13,7 +13,17 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Class, Client, Doc, DocumentQuery, Ref, RelatedDocument, toIdMap, TxOperations } from '@hcengineering/core'
|
||||
import {
|
||||
Class,
|
||||
Client,
|
||||
Doc,
|
||||
DocumentQuery,
|
||||
getCurrentAccount,
|
||||
Ref,
|
||||
RelatedDocument,
|
||||
toIdMap,
|
||||
TxOperations
|
||||
} from '@hcengineering/core'
|
||||
import { Resources, translate } from '@hcengineering/platform'
|
||||
import { getClient, MessageBox, ObjectSearchResult } from '@hcengineering/presentation'
|
||||
import { Issue, Milestone, Project } from '@hcengineering/tracker'
|
||||
@ -419,7 +429,8 @@ export default async (): Promise<Resources> => ({
|
||||
GetAllPriority: getAllPriority,
|
||||
GetAllComponents: getAllComponents,
|
||||
GetAllMilestones: getAllMilestones,
|
||||
GetVisibleFilters: getVisibleFilters
|
||||
GetVisibleFilters: getVisibleFilters,
|
||||
IsProjectJoined: async (project: Project) => !project.private || project.members.includes(getCurrentAccount()._id)
|
||||
},
|
||||
actionImpl: {
|
||||
Move: move,
|
||||
|
@ -21,11 +21,11 @@ import {
|
||||
CreateAggregationManagerFunc,
|
||||
GetAllValuesFunc,
|
||||
GrouppingManagerResource,
|
||||
KeyFilter,
|
||||
SortFunc,
|
||||
Viewlet,
|
||||
ViewletDescriptor,
|
||||
ViewQueryAction,
|
||||
KeyFilter
|
||||
Viewlet,
|
||||
ViewletDescriptor
|
||||
} from '@hcengineering/view'
|
||||
import tracker, { trackerId } from '../../tracker/lib'
|
||||
|
||||
@ -382,7 +382,8 @@ export default mergeIds(trackerId, tracker, {
|
||||
GetAllPriority: '' as GetAllValuesFunc,
|
||||
GetAllComponents: '' as GetAllValuesFunc,
|
||||
GetAllMilestones: '' as GetAllValuesFunc,
|
||||
GetVisibleFilters: '' as Resource<(filters: KeyFilter[], space?: Ref<Space>) => Promise<KeyFilter[]>>
|
||||
GetVisibleFilters: '' as Resource<(filters: KeyFilter[], space?: Ref<Space>) => Promise<KeyFilter[]>>,
|
||||
IsProjectJoined: '' as Resource<(space: Space) => Promise<boolean>>
|
||||
},
|
||||
aggregation: {
|
||||
CreateComponentAggregationManager: '' as CreateAggregationManagerFunc,
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Doc, Ref, Space, WithLookup } from '@hcengineering/core'
|
||||
import { Class, Doc, DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
|
||||
import { Asset, IntlString } from '@hcengineering/platform'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import {
|
||||
@ -50,6 +50,7 @@
|
||||
export let createComponentProps: Record<string, any> = {}
|
||||
export let isCreationDisabled = false
|
||||
export let descriptors: Ref<ViewletDescriptor>[] | undefined = undefined
|
||||
export let baseQuery: DocumentQuery<Doc> | undefined = undefined
|
||||
|
||||
let search = ''
|
||||
let viewlet: WithLookup<Viewlet> | undefined
|
||||
@ -116,7 +117,7 @@
|
||||
preferenceQuery.unsubscribe()
|
||||
}
|
||||
|
||||
$: query = viewlet?.baseQuery ?? {}
|
||||
$: query = { ...(baseQuery ?? {}), ...(viewlet?.baseQuery ?? {}) }
|
||||
$: searchQuery = search === '' ? query : { ...query, $search: search }
|
||||
$: resultQuery = searchQuery
|
||||
|
||||
@ -186,7 +187,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if !viewlet?.$lookup?.descriptor?.component || viewlet?.attachTo !== _class || (preference !== undefined && viewlet?._id !== preference.attachedTo)}}
|
||||
{#if !viewlet?.$lookup?.descriptor?.component || viewlet?.attachTo !== _class || (preference !== undefined && viewlet?._id !== preference.attachedTo)}
|
||||
<Loading />
|
||||
{:else}
|
||||
<FilterBar
|
||||
|
@ -117,6 +117,32 @@
|
||||
return await getResource(value.presenter)
|
||||
}
|
||||
}
|
||||
|
||||
let visibleIf: ((space: Space) => Promise<boolean>) | undefined
|
||||
|
||||
$: if (model.visibleIf) {
|
||||
getResource(model.visibleIf).then((r) => {
|
||||
visibleIf = r
|
||||
})
|
||||
}
|
||||
|
||||
let filteredSpaces: Space[] = []
|
||||
|
||||
async function updateSpaces (spaces: Space[], visibleIf: (space: Space) => Promise<boolean>): Promise<void> {
|
||||
const result: Space[] = []
|
||||
for (const s of spaces) {
|
||||
if (await visibleIf(s)) {
|
||||
result.push(s)
|
||||
}
|
||||
}
|
||||
filteredSpaces = result
|
||||
}
|
||||
|
||||
$: if (visibleIf) {
|
||||
updateSpaces(spaces, visibleIf)
|
||||
} else {
|
||||
filteredSpaces = spaces
|
||||
}
|
||||
</script>
|
||||
|
||||
<TreeNode
|
||||
@ -125,7 +151,7 @@
|
||||
actions={async () => getParentActions()}
|
||||
shortDropbox={model.specials !== undefined}
|
||||
>
|
||||
{#each spaces as space, i (space._id)}
|
||||
{#each filteredSpaces as space, i (space._id)}
|
||||
{#await getPresenter(space._class) then presenter}
|
||||
{#if separate && model.specials && i !== 0}<TreeSeparator line />{/if}
|
||||
{#if model.specials && presenter}
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Space } from '@hcengineering/core'
|
||||
import { AccountRole, Space, getCurrentAccount } from '@hcengineering/core'
|
||||
import { Resources } from '@hcengineering/platform'
|
||||
import ApplicationPresenter from './components/ApplicationPresenter.svelte'
|
||||
import Archive from './components/Archive.svelte'
|
||||
@ -42,7 +42,8 @@ export default async (): Promise<Resources> => ({
|
||||
Workbench
|
||||
},
|
||||
function: {
|
||||
HasArchiveSpaces: hasArchiveSpaces
|
||||
HasArchiveSpaces: hasArchiveSpaces,
|
||||
IsOwner: async (docs: Space[]) => getCurrentAccount().role === AccountRole.Owner
|
||||
},
|
||||
actionImpl: {
|
||||
Navigate: doNavigate
|
||||
|
@ -59,6 +59,8 @@ export interface SpacesNavModel {
|
||||
|
||||
// Child special items.
|
||||
specials?: SpecialNavModel[]
|
||||
|
||||
visibleIf?: Resource<(space: Space) => Promise<boolean>>
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,7 +97,6 @@ export function serveAccount (methods: Record<string, AccountMethod>, productId
|
||||
}
|
||||
const db = client.db(ACCOUNT_DB)
|
||||
const result = await method(db, productId, request, token)
|
||||
console.log(result)
|
||||
ctx.body = result
|
||||
})
|
||||
|
||||
|
@ -104,6 +104,7 @@ export interface Account {
|
||||
// Defined for server admins only
|
||||
admin?: boolean
|
||||
confirmed?: boolean
|
||||
lastWorkspace?: number
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,6 +116,7 @@ export interface Workspace {
|
||||
organisation: string
|
||||
accounts: ObjectId[]
|
||||
productId: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@ -285,6 +287,9 @@ export async function selectWorkspace (
|
||||
const workspaceInfo = await getWorkspace(db, productId, workspace)
|
||||
|
||||
if (workspaceInfo !== null) {
|
||||
if (workspaceInfo.disabled === true) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspace }))
|
||||
}
|
||||
const workspaces = accountInfo.workspaces
|
||||
|
||||
for (const w of workspaces) {
|
||||
@ -347,6 +352,7 @@ export async function join (
|
||||
): Promise<WorkspaceLoginInfo> {
|
||||
const invite = await getInvite(db, inviteId)
|
||||
const workspace = await checkInvite(invite, email)
|
||||
console.log(`join attempt:${email}, ${workspace.name}`)
|
||||
await assignWorkspace(db, productId, email, workspace.name)
|
||||
|
||||
const token = (await login(db, productId, email, password)).token
|
||||
@ -360,6 +366,7 @@ export async function join (
|
||||
*/
|
||||
export async function confirmEmail (db: Db, email: string): Promise<Account> {
|
||||
const account = await getAccount(db, email)
|
||||
console.log(`confirm email:${email}`)
|
||||
|
||||
if (account === null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: accountId }))
|
||||
@ -446,6 +453,7 @@ export async function signUpJoin (
|
||||
last: string,
|
||||
inviteId: ObjectId
|
||||
): Promise<WorkspaceLoginInfo> {
|
||||
console.log(`signup join:${email} ${first} ${last}`)
|
||||
const invite = await getInvite(db, inviteId)
|
||||
const workspace = await checkInvite(invite, email)
|
||||
await createAcc(db, productId, email, password, first, last, invite?.emailMask === email)
|
||||
@ -529,6 +537,7 @@ export async function createAccount (
|
||||
export async function listWorkspaces (db: Db, productId: string): Promise<WorkspaceInfoOnly[]> {
|
||||
return (await db.collection<Workspace>(WORKSPACE_COLLECTION).find(withProductId(productId, {})).toArray())
|
||||
.map((it) => ({ ...it, productId }))
|
||||
.filter((it) => it.disabled !== true)
|
||||
.map(trimWorkspace)
|
||||
}
|
||||
|
||||
@ -612,20 +621,59 @@ export async function upgradeWorkspace (
|
||||
export const createUserWorkspace =
|
||||
(version: Data<Version>, txes: Tx[], migrationOperation: [string, MigrateOperation][]) =>
|
||||
async (db: Db, productId: string, token: string, workspace: string): Promise<LoginInfo> => {
|
||||
if (!/^[0-9a-z][0-9a-z-]{2,62}[0-9a-z]$/.test(workspace)) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.InvalidId, { id: workspace }))
|
||||
}
|
||||
|
||||
const { email, extra } = decodeToken(token)
|
||||
if (extra?.confirmed === false) {
|
||||
const nonConfirmed = extra?.confirmed === false
|
||||
console.log(`Creating workspace ${workspace} for ${email} ${nonConfirmed ? 'non confirmed' : 'confirmed'}`)
|
||||
|
||||
if (nonConfirmed) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: email }))
|
||||
}
|
||||
await createWorkspace(version, txes, migrationOperation, db, productId, workspace, '')
|
||||
const info = await getAccount(db, email)
|
||||
if (info === null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: email }))
|
||||
}
|
||||
|
||||
if (info.lastWorkspace !== undefined) {
|
||||
if (Date.now() - info.lastWorkspace < 60 * 1000) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceRateLimit, { workspace }))
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await createWorkspace(version, txes, migrationOperation, db, productId, workspace, '')
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
// We need to drop workspace, to prevent wrong data usage.
|
||||
const ws = await getWorkspace(db, productId, workspace)
|
||||
if (ws === null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspace }))
|
||||
}
|
||||
await db.collection(WORKSPACE_COLLECTION).updateOne(
|
||||
{
|
||||
_id: ws._id
|
||||
},
|
||||
{ $set: { disabled: true } }
|
||||
)
|
||||
throw err
|
||||
}
|
||||
info.lastWorkspace = Date.now()
|
||||
|
||||
// Update last workspace time.
|
||||
await db.collection(ACCOUNT_COLLECTION).updateOne({ _id: info._id }, { $set: { lastWorkspace: Date.now() } })
|
||||
|
||||
await assignWorkspace(db, productId, email, workspace)
|
||||
await setRole(email, workspace, productId, AccountRole.Owner)
|
||||
const info = await getAccount(db, email)
|
||||
const result = {
|
||||
endpoint: getEndpoint(),
|
||||
email,
|
||||
token: generateToken(email, getWorkspaceId(workspace, productId), getExtra(info)),
|
||||
productId
|
||||
}
|
||||
console.log(`Creating workspace ${workspace} Done`)
|
||||
return result
|
||||
}
|
||||
|
||||
@ -678,7 +726,9 @@ export async function getUserWorkspaces (db: Db, productId: string, token: strin
|
||||
.collection<Workspace>(WORKSPACE_COLLECTION)
|
||||
.find(withProductId(productId, account.admin === true ? {} : { _id: { $in: account.workspaces } }))
|
||||
.toArray()
|
||||
).map(trimWorkspace)
|
||||
)
|
||||
.filter((it) => it.disabled !== true)
|
||||
.map(trimWorkspace)
|
||||
}
|
||||
|
||||
async function getWorkspaceAndAccount (
|
||||
|
@ -57,17 +57,24 @@ export class MinioService {
|
||||
}
|
||||
|
||||
async list (workspaceId: WorkspaceId, prefix?: string): Promise<MinioWorkspaceItem[]> {
|
||||
const items = new Map<string, BucketItem & { metaData: ItemBucketMetadata }>()
|
||||
const list = await this.client.listObjects(getBucketId(workspaceId), prefix, true)
|
||||
await new Promise((resolve) => {
|
||||
list.on('data', (data) => {
|
||||
items.set(data.name, { metaData: {}, ...data })
|
||||
try {
|
||||
const items = new Map<string, BucketItem & { metaData: ItemBucketMetadata }>()
|
||||
const list = await this.client.listObjects(getBucketId(workspaceId), prefix, true)
|
||||
await new Promise((resolve) => {
|
||||
list.on('data', (data) => {
|
||||
items.set(data.name, { metaData: {}, ...data })
|
||||
})
|
||||
list.on('end', () => {
|
||||
resolve(null)
|
||||
})
|
||||
})
|
||||
list.on('end', () => {
|
||||
resolve(null)
|
||||
})
|
||||
})
|
||||
return Array.from(items.values())
|
||||
return Array.from(items.values())
|
||||
} catch (err: any) {
|
||||
if (((err?.message as string) ?? '').includes('Invalid bucket name')) {
|
||||
return []
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async stat (workspaceId: WorkspaceId, objectName: string): Promise<BucketItemStat> {
|
||||
|
Loading…
Reference in New Issue
Block a user