mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-17 18:05:42 +02:00
UBERF-7090: Add QMS plugins (#5716)
Signed-off-by: Alexey Zinoviev <alexey.zinoviev@xored.com>
This commit is contained in:
Generated
+974
-2
File diff suppressed because it is too large
Load Diff
+19
-1
@@ -190,6 +190,24 @@
|
||||
"@hcengineering/guest": "^0.6.0",
|
||||
"@hcengineering/guest-assets": "^0.6.0",
|
||||
"@hcengineering/guest-resources": "^0.6.0",
|
||||
"@hcengineering/analytics": "^0.6.0"
|
||||
"@hcengineering/analytics": "^0.6.0",
|
||||
"@hcengineering/sign": "^0.6.0",
|
||||
"@hcengineering/print": "^0.6.0",
|
||||
"@hcengineering/print-assets": "^0.6.0",
|
||||
"@hcengineering/print-resources": "^0.6.0",
|
||||
"@hcengineering/controlled-documents": "^0.1.0",
|
||||
"@hcengineering/controlled-documents-assets": "^0.1.0",
|
||||
"@hcengineering/controlled-documents-resources": "^0.1.0",
|
||||
"@hcengineering/questions": "^0.1.0",
|
||||
"@hcengineering/questions-assets": "^0.1.0",
|
||||
"@hcengineering/questions-resources": "^0.1.0",
|
||||
"@hcengineering/training": "^0.1.0",
|
||||
"@hcengineering/training-assets": "^0.1.0",
|
||||
"@hcengineering/training-resources": "^0.1.0",
|
||||
"@hcengineering/server-training": "^0.1.0",
|
||||
"@hcengineering/server-training-resources": "^0.1.0",
|
||||
"@hcengineering/products": "~0.1.0",
|
||||
"@hcengineering/products-assets": "~0.1.0",
|
||||
"@hcengineering/products-resources": "~0.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,12 @@ import view, { viewId } from '@hcengineering/view'
|
||||
import workbench, { workbenchId } from '@hcengineering/workbench'
|
||||
|
||||
import { bitrixId } from '@hcengineering/bitrix'
|
||||
import print, { printId } from '@hcengineering/print'
|
||||
import sign from '@hcengineering/sign'
|
||||
import { productsId } from '@hcengineering/products'
|
||||
import { questionsId } from '@hcengineering/questions'
|
||||
import { trainingId } from '@hcengineering/training'
|
||||
import { documentsId } from '@hcengineering/controlled-documents'
|
||||
|
||||
import '@hcengineering/activity-assets'
|
||||
import '@hcengineering/attachment-assets'
|
||||
@@ -78,6 +84,11 @@ import '@hcengineering/time-assets'
|
||||
import '@hcengineering/tracker-assets'
|
||||
import '@hcengineering/view-assets'
|
||||
import '@hcengineering/workbench-assets'
|
||||
import '@hcengineering/print-assets'
|
||||
import '@hcengineering/questions-assets'
|
||||
import '@hcengineering/training-assets'
|
||||
import '@hcengineering/products-assets'
|
||||
import '@hcengineering/controlled-documents-assets'
|
||||
|
||||
import { coreId } from '@hcengineering/core'
|
||||
import presentation, { presentationId } from '@hcengineering/presentation'
|
||||
@@ -161,6 +172,11 @@ function configureI18n(): void {
|
||||
addStringsLoader(timeId, async (lang: string) => await import(`@hcengineering/time-assets/lang/${lang}.json`))
|
||||
addStringsLoader(documentId, async (lang: string) => await import(`@hcengineering/document-assets/lang/${lang}.json`))
|
||||
addStringsLoader(guestId, async (lang: string) => await import(`@hcengineering/guest-assets/lang/${lang}.json`))
|
||||
addStringsLoader(documentsId, async (lang: string) => await import(`@hcengineering/controlled-documents-assets/lang/${lang}.json`))
|
||||
addStringsLoader(productsId, async (lang: string) => await import(`@hcengineering/products-assets/lang/${lang}.json`))
|
||||
addStringsLoader(questionsId, async (lang: string) => await import(`@hcengineering/questions-assets/lang/${lang}.json`))
|
||||
addStringsLoader(trainingId, async (lang: string) => await import(`@hcengineering/training-assets/lang/${lang}.json`))
|
||||
addStringsLoader(printId, async (lang: string) => await import(`@hcengineering/print-assets/lang/${lang}.json`))
|
||||
}
|
||||
|
||||
export async function configurePlatform() {
|
||||
@@ -285,6 +301,11 @@ export async function configurePlatform() {
|
||||
addLocation(guestId, () => import(/* webpackChunkName: "guest" */ '@hcengineering/guest-resources'))
|
||||
addLocation(timeId, () => import(/* webpackChunkName: "time" */ '@hcengineering/time-resources'))
|
||||
addLocation(driveId, () => import(/* webpackChunkName: "drive" */ '@hcengineering/drive-resources'))
|
||||
addLocation(questionsId, () => import(/* webpackChunkName: "training" */ '@hcengineering/questions-resources'))
|
||||
addLocation(trainingId, () => import(/* webpackChunkName: "training" */ '@hcengineering/training-resources'))
|
||||
addLocation(productsId, () => import(/* webpackChunkName: "products" */ '@hcengineering/products-resources'))
|
||||
addLocation(documentsId, () => import(/* webpackChunkName: "documents" */ '@hcengineering/controlled-documents-resources'))
|
||||
addLocation(printId, () => import(/* webpackChunkName: "print" */ '@hcengineering/print-resources'))
|
||||
|
||||
setMetadata(client.metadata.FilterModel, true)
|
||||
setMetadata(client.metadata.ExtraPlugins, ['preference' as Plugin])
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
"lib": [
|
||||
"es2016",
|
||||
"dom",
|
||||
"ES2021.String"
|
||||
"ES2021.String",
|
||||
"ESNext.Array"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,14 @@
|
||||
"@hcengineering/model-time": "^0.6.0",
|
||||
"@hcengineering/model-server-time": "^0.6.0",
|
||||
"@hcengineering/model-drive": "^0.6.0",
|
||||
"@hcengineering/model-server-drive": "^0.6.0"
|
||||
"@hcengineering/model-server-drive": "^0.6.0",
|
||||
"@hcengineering/model-print": "^0.6.0",
|
||||
"@hcengineering/model-questions": "^0.1.0",
|
||||
"@hcengineering/model-training": "^0.1.0",
|
||||
"@hcengineering/model-controlled-documents": "^0.1.0",
|
||||
"@hcengineering/model-products": "^0.1.0",
|
||||
"@hcengineering/model-server-products": "^0.1.0",
|
||||
"@hcengineering/model-server-training": "^0.1.0",
|
||||
"@hcengineering/model-server-controlled-documents": "^0.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
+60
-1
@@ -79,6 +79,19 @@ import workbench, { workbenchId, createModel as workbenchModel } from '@hcengine
|
||||
import { openAIId, createModel as serverOpenAI } from '@hcengineering/model-server-openai'
|
||||
import { createModel as serverTranslate, translateId } from '@hcengineering/model-server-translate'
|
||||
|
||||
import { printId, createModel as printModel } from '@hcengineering/model-print'
|
||||
|
||||
import { questionsId, createModel as questionsModel } from '@hcengineering/model-questions'
|
||||
import trainings, { trainingId, createModel as trainingModel } from '@hcengineering/model-training'
|
||||
import documents, { documentsId, createModel as documentsModel } from '@hcengineering/model-controlled-documents'
|
||||
import products, { productsId, createModel as productsModel } from '@hcengineering/model-products'
|
||||
import { serverProductsId, createModel as serverProductsModel } from '@hcengineering/model-server-products'
|
||||
import { serverTrainingId, createModel as serverTrainingModel } from '@hcengineering/model-server-training'
|
||||
import {
|
||||
serverDocumentsId,
|
||||
createModel as serverDocumentsModel
|
||||
} from '@hcengineering/model-server-controlled-documents'
|
||||
|
||||
import { type Plugin } from '@hcengineering/platform'
|
||||
|
||||
interface ConfigurablePlugin extends Omit<Data<PluginConfiguration>, 'pluginId' | 'transactions'> {}
|
||||
@@ -298,8 +311,51 @@ export default function buildModel (enabled: string[] = ['*'], disabled: string[
|
||||
}
|
||||
],
|
||||
[timeModel, timeId],
|
||||
[printModel, printId],
|
||||
[driveModel, driveId],
|
||||
[supportModel, supportId],
|
||||
[
|
||||
documentsModel,
|
||||
documentsId,
|
||||
{
|
||||
label: documents.string.ConfigLabel,
|
||||
description: documents.string.ConfigDescription,
|
||||
enabled: false,
|
||||
beta: false,
|
||||
classFilter: defaultFilter
|
||||
}
|
||||
],
|
||||
[
|
||||
questionsModel,
|
||||
questionsId,
|
||||
{
|
||||
enabled: false,
|
||||
beta: false,
|
||||
classFilter: defaultFilter
|
||||
}
|
||||
],
|
||||
[
|
||||
trainingModel,
|
||||
trainingId,
|
||||
{
|
||||
label: trainings.string.ConfigLabel,
|
||||
description: trainings.string.ConfigDescription,
|
||||
enabled: false,
|
||||
beta: false,
|
||||
classFilter: defaultFilter
|
||||
}
|
||||
],
|
||||
[
|
||||
productsModel,
|
||||
productsId,
|
||||
{
|
||||
label: products.string.ConfigLabel,
|
||||
description: products.string.ConfigDescription,
|
||||
enabled: false,
|
||||
beta: false,
|
||||
classFilter: defaultFilter
|
||||
}
|
||||
],
|
||||
|
||||
[serverCoreModel, serverCoreId],
|
||||
[serverAttachmentModel, serverAttachmentId],
|
||||
@@ -327,7 +383,10 @@ export default function buildModel (enabled: string[] = ['*'], disabled: string[
|
||||
[serverDocumentModel, serverDocumentId],
|
||||
[serverTimeModel, serverTimeId],
|
||||
[serverGuestModel, serverGuestId],
|
||||
[serverDriveModel, serverDriveId]
|
||||
[serverDriveModel, serverDriveId],
|
||||
[serverProductsModel, serverProductsId],
|
||||
[serverTrainingModel, serverTrainingId],
|
||||
[serverDocumentsModel, serverDocumentsId]
|
||||
]
|
||||
|
||||
for (const [b, id, config] of builders) {
|
||||
|
||||
@@ -42,6 +42,10 @@ import { activityOperation } from '@hcengineering/model-activity'
|
||||
import { activityServerOperation } from '@hcengineering/model-server-activity'
|
||||
import { documentOperation } from '@hcengineering/model-document'
|
||||
import { textEditorOperation } from '@hcengineering/model-text-editor'
|
||||
import { questionsOperation } from '@hcengineering/model-questions'
|
||||
import { trainingOperation } from '@hcengineering/model-training'
|
||||
import { documentsOperation } from '@hcengineering/model-controlled-documents'
|
||||
import { productsOperation } from '@hcengineering/model-products'
|
||||
|
||||
export const migrateOperations: [string, MigrateOperation][] = [
|
||||
['core', coreOperation],
|
||||
@@ -62,6 +66,10 @@ export const migrateOperations: [string, MigrateOperation][] = [
|
||||
['tags', tagsOperation],
|
||||
['setting', settingOperation],
|
||||
['tracker', trackerOperation],
|
||||
['documents', documentsOperation],
|
||||
['questions', questionsOperation],
|
||||
['training', trainingOperation],
|
||||
['products', productsOperation],
|
||||
['board', boardOperation],
|
||||
['hr', hrOperation],
|
||||
['bitrix', bitrixOperation],
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig",
|
||||
"rigProfile": "model"
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "@hcengineering/model-controlled-documents",
|
||||
"version": "0.1.0",
|
||||
"main": "lib/index.js",
|
||||
"svelte": "src/index.ts",
|
||||
"types": "types/index.d.ts",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"template": "@hcengineering/model-package",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "compile",
|
||||
"build:watch": "compile",
|
||||
"format": "format src",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"prettier": "^3.1.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/attachment": "^0.6.9",
|
||||
"@hcengineering/model-core": "^0.6.0",
|
||||
"@hcengineering/model-chunter": "^0.6.0",
|
||||
"@hcengineering/model-workbench": "^0.6.1",
|
||||
"@hcengineering/model-attachment": "^0.6.0",
|
||||
"@hcengineering/model-view": "^0.6.0",
|
||||
"@hcengineering/model-presentation": "^0.6.0",
|
||||
"@hcengineering/model-tracker": "^0.6.0",
|
||||
"@hcengineering/model-contact": "^0.6.1",
|
||||
"@hcengineering/model-request": "^0.6.0",
|
||||
"@hcengineering/model-print": "^0.6.0",
|
||||
"@hcengineering/model": "^0.6.7",
|
||||
"@hcengineering/setting": "^0.6.11",
|
||||
"@hcengineering/core": "^0.6.28",
|
||||
"@hcengineering/ui": "^0.6.11",
|
||||
"@hcengineering/platform": "^0.6.9",
|
||||
"@hcengineering/view": "^0.6.9",
|
||||
"@hcengineering/contact": "^0.6.20",
|
||||
"@hcengineering/tags": "^0.6.12",
|
||||
"@hcengineering/request": "^0.6.6",
|
||||
"@hcengineering/activity": "^0.6.0",
|
||||
"@hcengineering/controlled-documents": "^0.1.0",
|
||||
"@hcengineering/controlled-documents-resources": "^0.1.0",
|
||||
"@hcengineering/training": "^0.1.0",
|
||||
"@hcengineering/notification": "^0.6.16",
|
||||
"@hcengineering/model-notification": "^0.6.0",
|
||||
"@hcengineering/chunter": "^0.6.12"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,718 @@
|
||||
//
|
||||
// Copyright @ 2022-2023 Hardcore Engineering Inc.
|
||||
//
|
||||
|
||||
import {
|
||||
type Data,
|
||||
type Ref,
|
||||
TxOperations,
|
||||
type Class,
|
||||
type Space,
|
||||
generateId,
|
||||
DOMAIN_TX,
|
||||
AccountRole,
|
||||
DOMAIN_DOC_INDEX_STATE,
|
||||
getCollaborativeDoc
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
createDefaultSpace,
|
||||
createOrUpdate,
|
||||
tryMigrate,
|
||||
type MigrateOperation,
|
||||
type MigrationClient,
|
||||
type MigrationUpgradeClient
|
||||
} from '@hcengineering/model'
|
||||
import core, { DOMAIN_SPACE } from '@hcengineering/model-core'
|
||||
import contact from '@hcengineering/model-contact'
|
||||
import tags from '@hcengineering/tags'
|
||||
import {
|
||||
type ChangeControl,
|
||||
type DocumentCategory,
|
||||
type Document,
|
||||
type DocumentMeta,
|
||||
type DocumentSpace,
|
||||
type HierarchyDocument,
|
||||
type ProjectDocument,
|
||||
type ProjectMeta,
|
||||
TEMPLATE_PREFIX,
|
||||
getDocumentId,
|
||||
DocumentState,
|
||||
documentsId,
|
||||
createDocumentTemplate,
|
||||
type ControlledDocument,
|
||||
createChangeControl
|
||||
} from '@hcengineering/controlled-documents'
|
||||
|
||||
import documents, { DOMAIN_DOCUMENTS } from './index'
|
||||
|
||||
async function migrateDocumentCategories (client: MigrationClient): Promise<void> {
|
||||
const oldSpace = 'documents:space:Documents' as Ref<Space>
|
||||
|
||||
await client.update(
|
||||
DOMAIN_DOCUMENTS,
|
||||
{
|
||||
_class: documents.class.DocumentCategory,
|
||||
space: oldSpace
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
space: documents.space.QualityDocuments
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function migrateUnusedDocumentSpaces (client: MigrationClient): Promise<void> {
|
||||
const spaces: Ref<DocumentSpace>[] = [
|
||||
'events:space:EventsDocuments' as Ref<DocumentSpace>,
|
||||
'clients:space:ClientsDocuments' as Ref<DocumentSpace>,
|
||||
'supplier:space:SuppliersDocuments' as Ref<DocumentSpace>,
|
||||
'trainings:space:TrainingsDocuments' as Ref<DocumentSpace>
|
||||
]
|
||||
|
||||
await client.update(
|
||||
DOMAIN_SPACE,
|
||||
{
|
||||
_id: { $in: spaces },
|
||||
_class: documents.class.DocumentSpace,
|
||||
archived: false
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
archived: true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function migrateProjectMeta (client: MigrationClient): Promise<void> {
|
||||
type ExDocumentMeta = DocumentMeta & { path: Ref<DocumentMeta>[] }
|
||||
|
||||
const metaToMigrate = await client.find<ExDocumentMeta>(DOMAIN_DOCUMENTS, {
|
||||
_class: documents.class.DocumentMeta,
|
||||
path: { $exists: true }
|
||||
})
|
||||
|
||||
for (const meta of metaToMigrate) {
|
||||
const projectMetaId: Ref<ProjectMeta> = generateId()
|
||||
|
||||
const docs = await client.find<HierarchyDocument>(DOMAIN_DOCUMENTS, {
|
||||
attachedTo: meta._id
|
||||
})
|
||||
|
||||
for (const doc of docs) {
|
||||
await client.create<ProjectDocument>(DOMAIN_DOCUMENTS, {
|
||||
_id: generateId(),
|
||||
_class: documents.class.ProjectDocument,
|
||||
space: doc.space,
|
||||
modifiedBy: core.account.System,
|
||||
modifiedOn: Date.now(),
|
||||
attachedTo: projectMetaId,
|
||||
attachedToClass: documents.class.ProjectMeta,
|
||||
collection: 'documents',
|
||||
project: documents.ids.NoProject,
|
||||
initial: documents.ids.NoProject,
|
||||
document: doc._id
|
||||
})
|
||||
}
|
||||
|
||||
await client.create<ProjectMeta>(DOMAIN_DOCUMENTS, {
|
||||
_id: projectMetaId,
|
||||
_class: documents.class.ProjectMeta,
|
||||
space: meta.space,
|
||||
modifiedBy: core.account.System,
|
||||
modifiedOn: Date.now(),
|
||||
project: documents.ids.NoProject,
|
||||
meta: meta._id,
|
||||
path: meta.path,
|
||||
parent: meta.path[0] ?? documents.ids.NoParent,
|
||||
documents: docs.length
|
||||
})
|
||||
|
||||
await client.update(
|
||||
DOMAIN_DOCUMENTS,
|
||||
{ _id: meta._id },
|
||||
{
|
||||
$unset: {
|
||||
path: undefined
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function migrateDocumentSpaces (client: MigrationClient): Promise<void> {
|
||||
const affectedClasses: Ref<Class<DocumentSpace>>[] = [
|
||||
documents.class.DocumentSpace,
|
||||
documents.class.OrgSpace,
|
||||
documents.class.ExternalSpace
|
||||
]
|
||||
|
||||
await client.update(
|
||||
DOMAIN_SPACE,
|
||||
{
|
||||
_class: { $in: affectedClasses },
|
||||
type: { $exists: false }
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
type: documents.spaceType.DocumentSpaceType
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
await client.update(
|
||||
DOMAIN_SPACE,
|
||||
{
|
||||
_class: { $in: affectedClasses },
|
||||
type: 'documents:spaceType:DefaultDocLibraryType'
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
type: documents.spaceType.DocumentSpaceType
|
||||
},
|
||||
$rename: {
|
||||
'documents:spaceType:DefaultDocLibraryType:type:mixin': 'documents:spaceType:DocumentSpaceType:type:mixin'
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function migrateLibraryDocumentSpace (client: MigrationClient): Promise<void> {
|
||||
await client.update(
|
||||
DOMAIN_SPACE,
|
||||
{
|
||||
_class: 'documents:class:LibraryDocumentSpace' as Ref<Class<DocumentSpace>>
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
_class: documents.class.DocumentSpace
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function createTemplatesSpace (tx: TxOperations): Promise<void> {
|
||||
const existingSpace = await tx.findOne(documents.class.DocumentSpace, {
|
||||
_id: documents.space.UnsortedTemplates
|
||||
})
|
||||
|
||||
if (existingSpace === undefined) {
|
||||
await tx.createDoc(
|
||||
documents.class.DocumentSpace,
|
||||
core.space.Space,
|
||||
{
|
||||
name: 'Unsorted templates',
|
||||
description: 'Unsorted templates',
|
||||
private: false,
|
||||
archived: false,
|
||||
autoJoin: true,
|
||||
members: [],
|
||||
type: documents.spaceType.DocumentSpaceType
|
||||
},
|
||||
documents.space.UnsortedTemplates
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function createQualityDocumentsSpace (tx: TxOperations): Promise<void> {
|
||||
const existingSpace = await tx.findOne(documents.class.OrgSpace, {
|
||||
_id: documents.space.QualityDocuments
|
||||
})
|
||||
|
||||
if (existingSpace === undefined) {
|
||||
await tx.createDoc(
|
||||
documents.class.OrgSpace,
|
||||
core.space.Space,
|
||||
{
|
||||
name: 'Quality documents',
|
||||
description: "Space for organization's quality documents",
|
||||
private: true,
|
||||
archived: false,
|
||||
members: [],
|
||||
autoJoin: true,
|
||||
owners: [],
|
||||
type: documents.spaceType.DocumentSpaceType
|
||||
},
|
||||
documents.space.QualityDocuments
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function migrateSpace (client: MigrationClient): Promise<void> {
|
||||
await client.update(
|
||||
DOMAIN_SPACE,
|
||||
{
|
||||
_id: documents.space.Documents,
|
||||
_class: documents.class.DocumentSpace
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
_class: core.class.Space
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function migrateEffectiveDates (client: MigrationClient): Promise<void> {
|
||||
await client.update(
|
||||
DOMAIN_DOCUMENTS,
|
||||
{
|
||||
effectiveDate: 0
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
plannedEffectiveDate: 0
|
||||
},
|
||||
$unset: {
|
||||
effectiveDate: undefined
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const docsToRelease = await client.find(DOMAIN_DOCUMENTS, { effectiveDate: { $gt: Date.now() } })
|
||||
|
||||
for (const doc of docsToRelease) {
|
||||
const plannedEffectiveDate = (doc as any).effectiveDate
|
||||
await client.update(
|
||||
DOMAIN_DOCUMENTS,
|
||||
{ _id: doc._id },
|
||||
{
|
||||
$set: {
|
||||
plannedEffectiveDate
|
||||
},
|
||||
$unset: {
|
||||
effectiveDate: undefined
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function migrateChangeControls (client: MigrationClient): Promise<void> {
|
||||
await client.update(
|
||||
DOMAIN_DOCUMENTS,
|
||||
{ _class: documents.class.ChangeControl, impactedDocuments: { $exists: false } },
|
||||
{
|
||||
$set: {
|
||||
impactedDocuments: []
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function migrateTemplatePrefixes (client: MigrationClient): Promise<void> {
|
||||
const templatesToMigrate = await client.find<Document>(DOMAIN_DOCUMENTS, {
|
||||
[documents.mixin.DocumentTemplate]: { $exists: true },
|
||||
prefix: { $ne: TEMPLATE_PREFIX }
|
||||
})
|
||||
|
||||
for (const tmp of templatesToMigrate) {
|
||||
await client.update(
|
||||
DOMAIN_DOCUMENTS,
|
||||
{
|
||||
_id: tmp._id
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
prefix: TEMPLATE_PREFIX,
|
||||
[`${documents.mixin.DocumentTemplate}.docPrefix`]: tmp.prefix
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function migrateDocumentCodes (client: MigrationClient): Promise<void> {
|
||||
const classes = client.hierarchy.getDescendants(documents.class.Document)
|
||||
const docsToMigrate = await client.find<Document>(DOMAIN_DOCUMENTS, {
|
||||
_class: { $in: classes },
|
||||
code: { $exists: false }
|
||||
})
|
||||
|
||||
for (const doc of docsToMigrate) {
|
||||
await client.update(
|
||||
DOMAIN_DOCUMENTS,
|
||||
{
|
||||
_id: doc._id
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
code: getDocumentId(doc)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function migrateCollaborativeDocument (client: MigrationClient): Promise<void> {
|
||||
const mixin = 'documents:mixin:CollaborativeDocument'
|
||||
|
||||
const docsToMigrate = await client.find(DOMAIN_DOCUMENTS, {
|
||||
[`${mixin}.document`]: { $exists: true }
|
||||
})
|
||||
|
||||
for (const doc of docsToMigrate) {
|
||||
const content = (doc as any)[mixin].document
|
||||
const snapshots = (doc as any)[mixin].snapshots ?? []
|
||||
|
||||
// move mixin document -> content field
|
||||
await client.update(
|
||||
DOMAIN_DOCUMENTS,
|
||||
{ _id: doc._id },
|
||||
{
|
||||
$set: {
|
||||
content,
|
||||
snapshots: snapshots.length
|
||||
},
|
||||
$unset: {
|
||||
[mixin]: undefined
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// make snapshots attached docs
|
||||
await client.update(
|
||||
DOMAIN_DOCUMENTS,
|
||||
{ _id: { $in: snapshots } },
|
||||
{
|
||||
$set: {
|
||||
attachedTo: doc._id,
|
||||
attachedToClass: doc._class,
|
||||
collection: 'snapshots'
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function fixChangeControlsForDocs (tx: TxOperations): Promise<void> {
|
||||
const defaultCCSpec: Data<ChangeControl> = {
|
||||
description: '',
|
||||
reason: '',
|
||||
impact: '',
|
||||
impactedDocuments: []
|
||||
}
|
||||
const controlledDocuments = await tx.findAll(
|
||||
documents.class.ControlledDocument,
|
||||
{},
|
||||
{ lookup: { changeControl: documents.class.ChangeControl } }
|
||||
)
|
||||
|
||||
for (const cdoc of controlledDocuments) {
|
||||
const existingCC = await tx.findOne(documents.class.ChangeControl, { _id: cdoc.changeControl })
|
||||
|
||||
if (existingCC !== undefined) {
|
||||
continue
|
||||
}
|
||||
|
||||
const newCc = await tx.createDoc(
|
||||
documents.class.ChangeControl,
|
||||
cdoc.space,
|
||||
defaultCCSpec,
|
||||
cdoc.changeControl?.length > 0 ? cdoc.changeControl : undefined
|
||||
)
|
||||
|
||||
if (cdoc.changeControl === undefined) {
|
||||
await tx.update(cdoc, { changeControl: newCc })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function createProductChangeControlTemplate (tx: TxOperations): Promise<void> {
|
||||
const ccCategory = 'documents:category:DOC - CC' as Ref<DocumentCategory>
|
||||
const productChangeControlTemplate = await tx.findOne(documents.mixin.DocumentTemplate, {
|
||||
category: ccCategory
|
||||
})
|
||||
|
||||
if (productChangeControlTemplate === undefined) {
|
||||
const ccRecordId = generateId<ChangeControl>()
|
||||
const ccRecord: Data<ChangeControl> = {
|
||||
description: '',
|
||||
reason: 'New template creation',
|
||||
impact: '',
|
||||
impactedDocuments: []
|
||||
}
|
||||
|
||||
const seq = await tx.findOne(documents.class.Sequence, {
|
||||
_id: documents.sequence.Templates
|
||||
})
|
||||
|
||||
if (seq === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const { success } = await createDocumentTemplate(
|
||||
tx,
|
||||
documents.class.ControlledDocument,
|
||||
documents.space.QualityDocuments,
|
||||
documents.mixin.DocumentTemplate,
|
||||
documents.ids.NoProject,
|
||||
undefined,
|
||||
documents.template.ProductChangeControl as unknown as Ref<ControlledDocument>,
|
||||
'CC',
|
||||
{
|
||||
title: 'Change Control Template for new Product Version',
|
||||
abstract:
|
||||
'This Template is to be used to create a Change Control document each time you want to create a new product version.',
|
||||
changeControl: ccRecordId,
|
||||
requests: 0,
|
||||
reviewers: [],
|
||||
approvers: [],
|
||||
coAuthors: [],
|
||||
code: `TMPL-${seq.sequence + 1}`,
|
||||
prefix: '',
|
||||
seqNumber: 0,
|
||||
major: 0,
|
||||
minor: 1,
|
||||
state: DocumentState.Effective,
|
||||
sections: 0,
|
||||
commentSequence: 0,
|
||||
content: getCollaborativeDoc(generateId())
|
||||
},
|
||||
ccCategory,
|
||||
undefined,
|
||||
{
|
||||
title: 'Section 1'
|
||||
}
|
||||
)
|
||||
|
||||
if (!success) {
|
||||
return
|
||||
}
|
||||
|
||||
await createChangeControl(tx, ccRecordId, ccRecord, documents.space.QualityDocuments)
|
||||
}
|
||||
}
|
||||
|
||||
async function createTemplateSequence (tx: TxOperations): Promise<void> {
|
||||
const templateSeq = await tx.findOne(documents.class.Sequence, {
|
||||
_id: documents.sequence.Templates
|
||||
})
|
||||
|
||||
if (templateSeq === undefined) {
|
||||
await tx.createDoc(
|
||||
documents.class.Sequence,
|
||||
documents.space.Documents,
|
||||
{
|
||||
attachedTo: documents.mixin.DocumentTemplate,
|
||||
sequence: 0
|
||||
},
|
||||
documents.sequence.Templates
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function createDocumentCategories (tx: TxOperations): Promise<void> {
|
||||
const categories: Pick<Data<DocumentCategory>, 'code' | 'title'>[] = [
|
||||
{ code: 'CA', title: 'CAPA (Corrective and Preventive Action)' },
|
||||
{ code: 'CC', title: 'Change Control' },
|
||||
{ code: 'CE', title: 'Clinical Evaluation, Post-Market Clinical Follow-Up' },
|
||||
{ code: 'CH', title: 'Complaint Handling & Support' },
|
||||
{ code: 'CS', title: 'Clincial Studies' },
|
||||
{ code: 'DC', title: 'Document-Control' },
|
||||
{ code: 'DI', title: 'Design-Input' },
|
||||
{ code: 'DT', title: 'Design Transfer' },
|
||||
{ code: 'HF', title: 'Human Factors' },
|
||||
{ code: 'HR', title: 'Human Resources' },
|
||||
{ code: 'HW', title: 'Hardware Development' },
|
||||
{ code: 'IA', title: 'Internal Audit' },
|
||||
{ code: 'IM', title: 'Installation & Maintenance' },
|
||||
{ code: 'IS', title: 'Infrastructure' },
|
||||
{ code: 'LA', title: 'Labeling' },
|
||||
{ code: 'MA', title: 'Marketing' },
|
||||
{ code: 'MR', title: 'Management Review' },
|
||||
{ code: 'PD', title: 'Product Development' },
|
||||
{ code: 'PM', title: 'Post-Market Surveillance' },
|
||||
{ code: 'PR', title: 'Production' },
|
||||
{ code: 'PS', title: 'Purchase and Supplier Management' },
|
||||
{ code: 'PSA', title: 'Product Safety' },
|
||||
{ code: 'QM', title: 'Quality Manual' },
|
||||
{ code: 'RM', title: 'Risk Management' },
|
||||
{ code: 'RU', title: 'Regulatory Update' },
|
||||
{ code: 'SA', title: 'Sales and Marketing' },
|
||||
{ code: 'SU', title: 'Support' },
|
||||
{ code: 'SW', title: 'Software Development' },
|
||||
{ code: 'TF', title: 'Technical File, Product Release' },
|
||||
{ code: 'VI', title: 'Vigilance' },
|
||||
{ code: 'VV', title: 'Verification & Validation' },
|
||||
{ code: 'CM', title: 'Client Management' }
|
||||
]
|
||||
|
||||
await Promise.all(
|
||||
categories.map((c) =>
|
||||
createOrUpdate(
|
||||
tx,
|
||||
documents.class.DocumentCategory,
|
||||
documents.space.QualityDocuments,
|
||||
{ ...c, attachments: 0 },
|
||||
((documents.category.DOC as string) + ' - ' + c.code) as Ref<DocumentCategory>
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
async function createTagCategories (tx: TxOperations): Promise<void> {
|
||||
await createOrUpdate(
|
||||
tx,
|
||||
tags.class.TagCategory,
|
||||
tags.space.Tags,
|
||||
{
|
||||
icon: tags.icon.Tags,
|
||||
label: 'Labels',
|
||||
targetClass: documents.class.Document,
|
||||
tags: [],
|
||||
default: true
|
||||
},
|
||||
documents.category.Other
|
||||
)
|
||||
|
||||
await createOrUpdate(
|
||||
tx,
|
||||
tags.class.TagCategory,
|
||||
tags.space.Tags,
|
||||
{
|
||||
icon: tags.icon.Tags,
|
||||
label: 'Labels',
|
||||
targetClass: documents.mixin.DocumentTemplate,
|
||||
tags: [],
|
||||
default: true
|
||||
},
|
||||
documents.category.OtherTemplate
|
||||
)
|
||||
}
|
||||
|
||||
async function migrateDocumentSpacesMixins (client: MigrationClient): Promise<void> {
|
||||
const oldSpaceTypeMixin = `${documents.spaceType.DocumentSpaceType}:type:mixin`
|
||||
const newSpaceTypeMixin = documents.mixin.DocumentSpaceTypeData
|
||||
const affectedClasses: Ref<Class<DocumentSpace>>[] = [
|
||||
documents.class.DocumentSpace,
|
||||
documents.class.OrgSpace,
|
||||
documents.class.ExternalSpace
|
||||
]
|
||||
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
objectClass: core.class.Attribute,
|
||||
'attributes.attributeOf': oldSpaceTypeMixin
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
'attributes.attributeOf': newSpaceTypeMixin
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
await client.update(
|
||||
DOMAIN_SPACE,
|
||||
{
|
||||
_class: { $in: affectedClasses },
|
||||
[oldSpaceTypeMixin]: { $exists: true }
|
||||
},
|
||||
{
|
||||
$rename: {
|
||||
[oldSpaceTypeMixin]: newSpaceTypeMixin
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function migrateDefaultProjectOwners (client: MigrationClient): Promise<void> {
|
||||
const workspaceOwners = await client.model.findAll(contact.class.PersonAccount, {
|
||||
role: AccountRole.Owner
|
||||
})
|
||||
|
||||
await client.update(
|
||||
DOMAIN_SPACE,
|
||||
{
|
||||
_id: documents.space.QualityDocuments,
|
||||
$or: [{ owners: { $exists: false } }, { owners: [core.account.System] }]
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
owners: workspaceOwners.map((it) => it._id)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function migrateMetaTitles (client: MigrationClient): Promise<void> {
|
||||
const targetMetas = await client.find<DocumentMeta>(DOMAIN_DOCUMENTS, { title: { $exists: false } })
|
||||
|
||||
for (const meta of targetMetas) {
|
||||
let targetDoc = (
|
||||
await client.find<Document>(DOMAIN_DOCUMENTS, { attachedTo: meta._id, state: DocumentState.Effective })
|
||||
)[0]
|
||||
|
||||
if (targetDoc === undefined) {
|
||||
targetDoc = (await client.find<Document>(DOMAIN_DOCUMENTS, { attachedTo: meta._id }))[0]
|
||||
}
|
||||
|
||||
if (targetDoc === undefined) {
|
||||
continue
|
||||
}
|
||||
|
||||
await client.update(
|
||||
DOMAIN_DOCUMENTS,
|
||||
{ _id: meta._id },
|
||||
{
|
||||
$set: {
|
||||
title: `${getDocumentId(targetDoc)} ${targetDoc.title}`
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function migrateMetaIndexState (client: MigrationClient): Promise<void> {
|
||||
await client.update(
|
||||
DOMAIN_DOC_INDEX_STATE,
|
||||
{
|
||||
_class: core.class.DocIndexState,
|
||||
objectClass: documents.class.DocumentMeta
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
stages: {}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export const documentsOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {
|
||||
await migrateSpace(client)
|
||||
await migrateEffectiveDates(client)
|
||||
await migrateChangeControls(client)
|
||||
await migrateTemplatePrefixes(client)
|
||||
// Note: Order matters! It is important to migrate template prefixes first.
|
||||
await migrateDocumentCodes(client)
|
||||
await migrateCollaborativeDocument(client)
|
||||
await migrateLibraryDocumentSpace(client)
|
||||
await migrateDocumentSpaces(client)
|
||||
await migrateDocumentCategories(client)
|
||||
await migrateUnusedDocumentSpaces(client)
|
||||
await migrateProjectMeta(client)
|
||||
await migrateDocumentSpacesMixins(client)
|
||||
await migrateDefaultProjectOwners(client)
|
||||
await migrateMetaTitles(client)
|
||||
await tryMigrate(client, documentsId, [
|
||||
{
|
||||
state: 'migrateMetaIndexState',
|
||||
func: migrateMetaIndexState
|
||||
}
|
||||
])
|
||||
},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||
const tx = new TxOperations(client, core.account.System)
|
||||
await createDefaultSpace(client, documents.space.Documents, { name: 'Documents', description: 'Documents' })
|
||||
await createQualityDocumentsSpace(tx)
|
||||
await createTemplatesSpace(tx)
|
||||
await createTemplateSequence(tx)
|
||||
await createTagCategories(tx)
|
||||
await createDocumentCategories(tx)
|
||||
await fixChangeControlsForDocs(tx)
|
||||
await createProductChangeControlTemplate(tx)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/core'
|
||||
import documents from '@hcengineering/controlled-documents'
|
||||
|
||||
export function definePermissions (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.ReviewDocumentPermission,
|
||||
description: documents.string.ReviewDocumentDescription
|
||||
},
|
||||
documents.permission.ReviewDocument
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.ApproveDocumentPermission,
|
||||
description: documents.string.ApproveDocumentDescription
|
||||
},
|
||||
documents.permission.ApproveDocument
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.CoAuthorDocumentPermission,
|
||||
description: documents.string.CoAuthorDocumentDescription
|
||||
},
|
||||
documents.permission.CoAuthorDocument
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.CreateDocumentPermission,
|
||||
description: documents.string.CreateDocumentDescription
|
||||
},
|
||||
documents.permission.CreateDocument
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.UpdateDocumentOwnerPermission,
|
||||
description: documents.string.UpdateDocumentOwnerDescription
|
||||
},
|
||||
documents.permission.UpdateDocumentOwner
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.CreateDocumentCategoryPermission,
|
||||
description: documents.string.CreateDocumentCategoryDescription
|
||||
},
|
||||
documents.permission.CreateDocumentCategory
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.UpdateDocumentCategoryPermission,
|
||||
description: documents.string.UpdateDocumentCategoryDescription
|
||||
},
|
||||
documents.permission.UpdateDocumentCategory
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.DeleteDocumentCategoryPermission,
|
||||
description: documents.string.DeleteDocumentCategoryDescription
|
||||
},
|
||||
documents.permission.DeleteDocumentCategory
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// Copyright © 2023 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.
|
||||
//
|
||||
|
||||
import { documentsId } from '@hcengineering/controlled-documents'
|
||||
import documents from '@hcengineering/controlled-documents-resources/src/plugin'
|
||||
import type { Client, Doc, Ref, Role } from '@hcengineering/core'
|
||||
import { type ObjectSearchCategory, type ObjectSearchFactory } from '@hcengineering/model-presentation'
|
||||
import { mergeIds, type Resource } from '@hcengineering/platform'
|
||||
import { type TagCategory } from '@hcengineering/tags'
|
||||
import { type AnyComponent } from '@hcengineering/ui'
|
||||
import { type ActionCategory, type ViewAction } from '@hcengineering/view'
|
||||
import { type NotificationType, type NotificationGroup } from '@hcengineering/notification'
|
||||
|
||||
export default mergeIds(documentsId, documents, {
|
||||
component: {
|
||||
DocumentTemplateSectionPresenter: '' as AnyComponent,
|
||||
ContentSectionPresenter: '' as AnyComponent,
|
||||
AttachmentSectionPresenter: '' as AnyComponent,
|
||||
DocumentVersions: '' as AnyComponent,
|
||||
EditDocumentContent: '' as AnyComponent,
|
||||
EditDocumentAttachment: '' as AnyComponent,
|
||||
TemplateSectionPresenter: '' as AnyComponent,
|
||||
|
||||
// new model components
|
||||
CategoryPresenter: '' as AnyComponent,
|
||||
Categories: '' as AnyComponent,
|
||||
DocumentTemplates: '' as AnyComponent,
|
||||
CollaborativeSectionPresenter: '' as AnyComponent,
|
||||
AttachmentsSectionPresenter: '' as AnyComponent,
|
||||
StateFilterValuePresenter: '' as AnyComponent,
|
||||
ControlledStateFilterValuePresenter: '' as AnyComponent,
|
||||
|
||||
// Projects
|
||||
Projects: '' as AnyComponent
|
||||
},
|
||||
completion: {
|
||||
DocumentMetaQuery: '' as Resource<ObjectSearchFactory>,
|
||||
DocumentMetaCategory: '' as Ref<ObjectSearchCategory>
|
||||
},
|
||||
category: {
|
||||
Document: '' as Ref<ActionCategory>,
|
||||
Other: '' as Ref<TagCategory>,
|
||||
OtherTemplate: '' as Ref<TagCategory>
|
||||
},
|
||||
function: {
|
||||
DocumentIdentifierProvider: '' as Resource<<T extends Doc>(client: Client, ref: Ref<T>, doc?: T) => Promise<string>>
|
||||
},
|
||||
actionImpl: {
|
||||
AddCollaborativeSectionAbove: '' as ViewAction,
|
||||
AddCollaborativeSectionBelow: '' as ViewAction,
|
||||
DeleteCollaborativeSection: '' as ViewAction,
|
||||
Duplicate: '' as ViewAction,
|
||||
EditDescription: '' as ViewAction,
|
||||
EditGuidance: '' as ViewAction,
|
||||
CreateChildDocument: '' as ViewAction,
|
||||
CreateChildTemplate: '' as ViewAction,
|
||||
CreateDocument: '' as ViewAction,
|
||||
CreateTemplate: '' as ViewAction,
|
||||
DeleteDocument: '' as ViewAction,
|
||||
EditDocSpace: '' as ViewAction
|
||||
},
|
||||
viewlet: {
|
||||
TableDocument: '' as Ref<Doc>,
|
||||
ListDocument: '' as Ref<Doc>,
|
||||
TableDocumentTemplate: '' as Ref<Doc>,
|
||||
TableDocumentDomain: '' as Ref<Doc>
|
||||
},
|
||||
role: {
|
||||
QARA: '' as Ref<Role>,
|
||||
Manager: '' as Ref<Role>,
|
||||
QualifiedUser: '' as Ref<Role>
|
||||
},
|
||||
notification: {
|
||||
DocumentsNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
ContentNotification: '' as Ref<NotificationType>,
|
||||
StateNotification: '' as Ref<NotificationType>,
|
||||
CoAuthorsNotification: '' as Ref<NotificationType>
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
|
||||
import core from '@hcengineering/core'
|
||||
|
||||
import documents from './plugin'
|
||||
|
||||
export const roles = [
|
||||
{
|
||||
_id: documents.role.QualifiedUser,
|
||||
name: 'Qualified User',
|
||||
permissions: [
|
||||
documents.permission.ReviewDocument,
|
||||
documents.permission.ApproveDocument,
|
||||
documents.permission.CoAuthorDocument
|
||||
]
|
||||
},
|
||||
{
|
||||
_id: documents.role.Manager,
|
||||
name: 'Manager',
|
||||
permissions: [
|
||||
documents.permission.CreateDocument,
|
||||
documents.permission.ReviewDocument,
|
||||
documents.permission.ApproveDocument,
|
||||
documents.permission.CoAuthorDocument,
|
||||
documents.permission.CreateDocumentCategory,
|
||||
documents.permission.UpdateDocumentCategory,
|
||||
documents.permission.DeleteDocumentCategory,
|
||||
core.permission.UpdateSpace
|
||||
]
|
||||
},
|
||||
{
|
||||
_id: documents.role.QARA,
|
||||
name: 'QARA',
|
||||
permissions: [
|
||||
documents.permission.CreateDocument,
|
||||
documents.permission.ReviewDocument,
|
||||
documents.permission.ApproveDocument,
|
||||
documents.permission.CoAuthorDocument,
|
||||
documents.permission.UpdateDocumentOwner,
|
||||
documents.permission.CreateDocumentCategory,
|
||||
documents.permission.UpdateDocumentCategory,
|
||||
documents.permission.DeleteDocumentCategory,
|
||||
core.permission.UpdateSpace
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// Copyright © 2024 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.
|
||||
//
|
||||
import { Prop, type Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/model-core'
|
||||
import { getRoleAttributeProps } from '@hcengineering/setting'
|
||||
import { type Permission, type Ref } from '@hcengineering/core'
|
||||
|
||||
import { TDocumentSpaceTypeData } from './types'
|
||||
import documents from './plugin'
|
||||
import { roles } from './roles'
|
||||
|
||||
export const documentPermissions: Ref<Permission>[] = [
|
||||
documents.permission.CreateDocument,
|
||||
documents.permission.ReviewDocument,
|
||||
documents.permission.ApproveDocument,
|
||||
documents.permission.CoAuthorDocument,
|
||||
documents.permission.UpdateDocumentOwner,
|
||||
documents.permission.CreateDocumentCategory,
|
||||
documents.permission.UpdateDocumentCategory,
|
||||
documents.permission.DeleteDocumentCategory,
|
||||
core.permission.UpdateSpace
|
||||
]
|
||||
|
||||
export function defineSpaceType (builder: Builder): void {
|
||||
for (const role of roles) {
|
||||
const { label, roleType } = getRoleAttributeProps(role.name)
|
||||
|
||||
Prop(roleType, label)(TDocumentSpaceTypeData.prototype, role._id)
|
||||
}
|
||||
|
||||
builder.createModel(TDocumentSpaceTypeData)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.SpaceTypeDescriptor,
|
||||
core.space.Model,
|
||||
{
|
||||
name: documents.string.DocumentApplication,
|
||||
description: documents.string.Description,
|
||||
icon: documents.icon.Document,
|
||||
baseClass: documents.class.DocumentSpace,
|
||||
availablePermissions: [...documentPermissions]
|
||||
},
|
||||
documents.descriptor.DocumentSpaceType
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
documents.class.DocumentSpaceType,
|
||||
core.space.Model,
|
||||
{
|
||||
name: 'Default Documents',
|
||||
descriptor: documents.descriptor.DocumentSpaceType,
|
||||
roles: roles.length,
|
||||
projects: false,
|
||||
targetClass: documents.mixin.DocumentSpaceTypeData
|
||||
},
|
||||
documents.spaceType.DocumentSpaceType
|
||||
)
|
||||
|
||||
for (const role of roles) {
|
||||
builder.createDoc(
|
||||
core.class.Role,
|
||||
core.space.Model,
|
||||
{
|
||||
attachedTo: documents.spaceType.DocumentSpaceType,
|
||||
attachedToClass: documents.class.DocumentSpaceType,
|
||||
collection: 'roles',
|
||||
name: role.name,
|
||||
permissions: role.permissions
|
||||
},
|
||||
role._id
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,590 @@
|
||||
//
|
||||
// Copyright © 2024 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.
|
||||
//
|
||||
|
||||
import request from '@hcengineering/request'
|
||||
import {
|
||||
type AttachmentsDocumentSection,
|
||||
type ChangeControl,
|
||||
type CollaborativeDocumentSection,
|
||||
type ControlledDocument,
|
||||
type ControlledDocumentState,
|
||||
type Document,
|
||||
type HierarchyDocument,
|
||||
type DocumentApprovalRequest,
|
||||
type DocumentCategory,
|
||||
type DocumentRequest,
|
||||
type DocumentReviewRequest,
|
||||
type DocumentSection,
|
||||
type DocumentComment,
|
||||
type DocumentSectionCreator,
|
||||
type DocumentSectionEditor,
|
||||
type DocumentSectionPresenter,
|
||||
type DocumentSpace,
|
||||
type DocumentSpaceType,
|
||||
type DocumentSpaceTypeDescriptor,
|
||||
type DocumentState,
|
||||
type DocumentTemplate,
|
||||
type DocumentTemplateSection,
|
||||
type Sequence,
|
||||
type DocumentMeta,
|
||||
type ExternalSpace,
|
||||
type OrgSpace,
|
||||
type Project,
|
||||
type ProjectDocument,
|
||||
type ProjectMeta,
|
||||
type DocumentTraining,
|
||||
type DocumentSnapshot,
|
||||
type ControlledDocumentSnapshot
|
||||
} from '@hcengineering/controlled-documents'
|
||||
import { TRequest } from '@hcengineering/model-request'
|
||||
import { type Attachment } from '@hcengineering/attachment'
|
||||
import contact, { type Employee } from '@hcengineering/contact'
|
||||
import {
|
||||
DateRangeMode,
|
||||
IndexKind,
|
||||
type AttachedData,
|
||||
type Class,
|
||||
type Doc,
|
||||
type Markup,
|
||||
type Ref,
|
||||
type Timestamp,
|
||||
type Type,
|
||||
type CollectionSize,
|
||||
type CollaborativeDoc,
|
||||
type Role,
|
||||
type TypedSpace,
|
||||
type Account,
|
||||
type RolesAssignment,
|
||||
type Domain
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
ArrOf,
|
||||
Collection,
|
||||
Hidden,
|
||||
Index,
|
||||
Mixin,
|
||||
Model,
|
||||
Prop,
|
||||
TypeBoolean,
|
||||
TypeDate,
|
||||
TypeNumber,
|
||||
TypeRef,
|
||||
TypeString,
|
||||
UX,
|
||||
TypeCollaborativeDoc,
|
||||
TypeMarkup,
|
||||
ReadOnly
|
||||
} from '@hcengineering/model'
|
||||
import attachment from '@hcengineering/model-attachment'
|
||||
import chunter, { TChatMessage } from '@hcengineering/model-chunter'
|
||||
import core, {
|
||||
TAttachedDoc,
|
||||
TClass,
|
||||
TDoc,
|
||||
TTypedSpace,
|
||||
TType,
|
||||
TSpaceTypeDescriptor,
|
||||
TSpaceType
|
||||
} from '@hcengineering/model-core'
|
||||
import { getEmbeddedLabel, type Resource } from '@hcengineering/platform'
|
||||
import tags, { type TagReference } from '@hcengineering/tags'
|
||||
import { type AnyComponent } from '@hcengineering/ui'
|
||||
import training, { type Training, type TrainingRequest } from '@hcengineering/training'
|
||||
|
||||
import documents from './plugin'
|
||||
|
||||
export const DOMAIN_DOCUMENTS = 'documents' as Domain
|
||||
|
||||
@Model(documents.class.DocumentSpace, core.class.TypedSpace)
|
||||
@UX(core.string.Space)
|
||||
export class TDocumentSpace extends TTypedSpace implements DocumentSpace {
|
||||
@Prop(TypeRef(documents.class.DocumentSpaceType), documents.string.DocumentSpaceType)
|
||||
declare type: Ref<DocumentSpaceType>
|
||||
}
|
||||
|
||||
@Model(documents.class.DocumentSpaceType, core.class.SpaceType)
|
||||
export class TDocumentSpaceType extends TSpaceType implements DocumentSpaceType {
|
||||
@Prop(TypeRef(documents.class.DocumentSpaceTypeDescriptor), getEmbeddedLabel('Descriptor'))
|
||||
declare descriptor: Ref<DocumentSpaceTypeDescriptor>
|
||||
|
||||
@Prop(TypeRef(core.class.Class), getEmbeddedLabel('Target Class'))
|
||||
declare targetClass: Ref<Class<DocumentSpace>>
|
||||
|
||||
@Prop(TypeBoolean(), getEmbeddedLabel('Projects'))
|
||||
projects!: boolean
|
||||
}
|
||||
|
||||
@Model(documents.class.DocumentSpaceTypeDescriptor, core.class.SpaceTypeDescriptor)
|
||||
export class TDocumentSpaceTypeDescriptor extends TSpaceTypeDescriptor implements DocumentSpaceTypeDescriptor {
|
||||
projectClass?: Ref<Class<Project>>
|
||||
}
|
||||
|
||||
@Model(documents.class.OrgSpace, documents.class.DocumentSpace)
|
||||
export class TOrgSpace extends TDocumentSpace implements OrgSpace {}
|
||||
|
||||
@Model(documents.class.ExternalSpace, documents.class.DocumentSpace)
|
||||
export class TExternalSpace extends TDocumentSpace implements ExternalSpace {}
|
||||
|
||||
@Model(documents.class.Project, core.class.Doc, DOMAIN_DOCUMENTS)
|
||||
export class TProject extends TDoc implements Project {
|
||||
@Prop(TypeRef(documents.class.DocumentSpace), documents.string.Space)
|
||||
@Index(IndexKind.Indexed)
|
||||
declare space: Ref<DocumentSpace>
|
||||
|
||||
@Prop(TypeString(), documents.string.Name)
|
||||
@Index(IndexKind.FullText)
|
||||
@ReadOnly()
|
||||
name!: string
|
||||
|
||||
@Prop(TypeBoolean(), documents.string.Readonly)
|
||||
@ReadOnly()
|
||||
@Hidden()
|
||||
readonly!: boolean
|
||||
}
|
||||
|
||||
@Model(documents.class.DocumentMeta, core.class.Doc, DOMAIN_DOCUMENTS)
|
||||
@UX(documents.string.Document)
|
||||
export class TDocumentMeta extends TDoc implements DocumentMeta {
|
||||
@Prop(Collection(documents.class.Document), documents.string.Documents)
|
||||
documents!: CollectionSize<Document>
|
||||
|
||||
@Prop(TypeString(), documents.string.Title)
|
||||
@Index(IndexKind.FullText)
|
||||
title!: string
|
||||
}
|
||||
|
||||
@Model(documents.class.ProjectMeta, core.class.Doc, DOMAIN_DOCUMENTS)
|
||||
@UX(documents.string.Project)
|
||||
export class TProjectMeta extends TDoc implements ProjectMeta {
|
||||
@Prop(TypeRef(documents.class.Project), documents.string.Project)
|
||||
@Index(IndexKind.Indexed)
|
||||
project!: Ref<Project>
|
||||
|
||||
@Prop(TypeRef(documents.class.DocumentMeta), documents.string.Document)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
meta!: Ref<DocumentMeta>
|
||||
|
||||
@Prop(ArrOf(TypeRef(documents.class.DocumentMeta)), documents.string.Path)
|
||||
path!: Ref<DocumentMeta>[]
|
||||
|
||||
@Prop(TypeRef(documents.class.DocumentMeta), documents.string.Parent)
|
||||
@Index(IndexKind.Indexed)
|
||||
parent!: Ref<DocumentMeta>
|
||||
|
||||
// @Prop(TypeRef(documents.class.Document), documents.string.Document)
|
||||
// @Index(IndexKind.Indexed)
|
||||
// head!: Ref<HierarchyDocument>
|
||||
|
||||
@Prop(Collection(documents.class.ProjectDocument), documents.string.Documents)
|
||||
documents!: CollectionSize<ProjectDocument>
|
||||
}
|
||||
|
||||
@Model(documents.class.ProjectDocument, core.class.AttachedDoc, DOMAIN_DOCUMENTS)
|
||||
@UX(documents.string.Document)
|
||||
export class TProjectDocument extends TAttachedDoc implements ProjectDocument {
|
||||
@Prop(TypeRef(core.class.Space), core.string.Space)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare space: Ref<DocumentSpace>
|
||||
|
||||
@Prop(TypeRef(documents.class.ProjectMeta), documents.string.Project)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare attachedTo: Ref<ProjectMeta>
|
||||
|
||||
@Prop(TypeString(), core.string.Collection)
|
||||
@Hidden()
|
||||
override collection: 'documents' = 'documents'
|
||||
|
||||
@Prop(TypeRef(documents.class.Project), documents.string.Project)
|
||||
@Index(IndexKind.Indexed)
|
||||
project!: Ref<Project>
|
||||
|
||||
@Prop(TypeRef(documents.class.Project), documents.string.Project)
|
||||
@Hidden()
|
||||
initial!: Ref<Project>
|
||||
|
||||
@Prop(TypeRef(documents.class.Document), documents.string.Document)
|
||||
@Index(IndexKind.Indexed)
|
||||
document!: Ref<HierarchyDocument>
|
||||
}
|
||||
|
||||
@Model(documents.class.Document, core.class.Doc, DOMAIN_DOCUMENTS)
|
||||
@UX(documents.string.Document, documents.icon.Document)
|
||||
export class TDocument extends TDoc implements Document {
|
||||
@Prop(TypeRef(core.class.Space), core.string.Space)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare space: Ref<DocumentSpace>
|
||||
|
||||
@Prop(TypeRef(documents.mixin.DocumentTemplate), documents.string.DocumentTemplate)
|
||||
@Hidden()
|
||||
template?: Ref<DocumentTemplate>
|
||||
|
||||
@Prop(TypeString(), documents.string.Title)
|
||||
@Index(IndexKind.FullText)
|
||||
title!: string
|
||||
|
||||
@Prop(TypeString(), documents.string.Title)
|
||||
@Index(IndexKind.FullText)
|
||||
code!: string
|
||||
|
||||
@Prop(TypeString(), documents.string.Code)
|
||||
@Index(IndexKind.FullText)
|
||||
prefix!: string
|
||||
|
||||
@Prop(TypeNumber(), documents.string.Number)
|
||||
@Hidden()
|
||||
seqNumber!: number
|
||||
|
||||
@Prop(TypeNumber(), documents.string.Major)
|
||||
major!: number
|
||||
|
||||
@Prop(TypeNumber(), documents.string.Minor)
|
||||
minor!: number
|
||||
|
||||
@Prop(TypeRef(documents.class.DocumentCategory), documents.string.Category)
|
||||
category?: Ref<DocumentCategory>
|
||||
|
||||
@Prop(TypeRef(contact.mixin.Employee), documents.string.Author)
|
||||
author?: Ref<Employee>
|
||||
|
||||
@Prop(TypeRef(contact.mixin.Employee), documents.string.Owner)
|
||||
owner?: Ref<Employee>
|
||||
|
||||
@Prop(TypeDocumentState(), documents.string.Status)
|
||||
state!: DocumentState
|
||||
|
||||
@Prop(TypeCollaborativeDoc(), documents.string.CollaborativeDocument)
|
||||
@Hidden()
|
||||
content!: CollaborativeDoc
|
||||
|
||||
@Prop(Collection(tags.class.TagReference), documents.string.Labels)
|
||||
labels?: CollectionSize<TagReference>
|
||||
|
||||
@Prop(Collection(documents.class.DocumentSection), documents.string.Sections)
|
||||
sections!: CollectionSize<DocumentSection>
|
||||
|
||||
@Prop(TypeString(), documents.string.MetaAbstract)
|
||||
@Index(IndexKind.FullText)
|
||||
abstract?: string
|
||||
|
||||
@Prop(TypeNumber(), documents.string.Number)
|
||||
@Hidden()
|
||||
commentSequence!: number
|
||||
|
||||
@Prop(Collection(documents.class.DocumentComment), chunter.string.Comments)
|
||||
comments?: CollectionSize<DocumentComment>
|
||||
|
||||
@Prop(Collection(documents.class.DocumentSnapshot), documents.string.Snapshots)
|
||||
snapshots?: CollectionSize<DocumentSnapshot>
|
||||
}
|
||||
|
||||
@Model(documents.class.HierarchyDocument, documents.class.Document)
|
||||
@UX(documents.string.Document, documents.icon.Document)
|
||||
export class THierarchyDocument extends TDocument implements HierarchyDocument {
|
||||
@Prop(TypeRef(documents.class.DocumentMeta), core.string.AttachedTo)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
attachedTo!: Ref<DocumentMeta>
|
||||
|
||||
@Prop(TypeRef(core.class.Class), core.string.AttachedToClass)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
attachedToClass!: Ref<Class<DocumentMeta>>
|
||||
|
||||
@Prop(TypeString(), core.string.Collection)
|
||||
@Hidden()
|
||||
collection!: 'documents'
|
||||
}
|
||||
|
||||
@Mixin(documents.mixin.DocumentTemplate, documents.class.Document)
|
||||
@UX(documents.string.DocumentTemplate, documents.icon.Document)
|
||||
export class TDocumentTemplate extends TDocument implements DocumentTemplate {
|
||||
@Prop(TypeNumber(), core.string.Collection)
|
||||
@Hidden()
|
||||
sequence!: number
|
||||
|
||||
@Prop(TypeString(), documents.string.DocumentPrefix)
|
||||
@Index(IndexKind.FullText)
|
||||
docPrefix!: string
|
||||
}
|
||||
|
||||
@Mixin(documents.mixin.DocumentTraining, documents.class.Document)
|
||||
@UX(training.string.Training, training.icon.Training)
|
||||
export class TDocumentTraining extends TDocument implements DocumentTraining {
|
||||
@Prop(TypeBoolean(), documents.string.DocumentTrainingEnabled, { defaultValue: false })
|
||||
enabled: boolean = false
|
||||
|
||||
@Prop(TypeRef(training.class.Training), training.string.Training, { defaultValue: null })
|
||||
training: Ref<Training> | null = null
|
||||
|
||||
@Prop(ArrOf(TypeRef(core.class.Role)), training.string.TrainingRequestRoles, { defaultValue: [] })
|
||||
roles: Array<Ref<Role>> = []
|
||||
|
||||
@Prop(ArrOf(TypeRef(contact.mixin.Employee)), training.string.TrainingRequestTrainees, { defaultValue: [] })
|
||||
trainees: TrainingRequest['trainees'] = []
|
||||
|
||||
@Prop(TypeNumber(), training.string.TrainingRequestMaxAttempts, { defaultValue: null })
|
||||
maxAttempts: TrainingRequest['maxAttempts'] = null
|
||||
|
||||
@Prop(TypeNumber(), documents.string.DocumentTrainingDueDays, { defaultValue: null })
|
||||
dueDays: number | null = null
|
||||
}
|
||||
|
||||
@Model(documents.class.DocumentCategory, core.class.Doc, DOMAIN_DOCUMENTS)
|
||||
@UX(documents.string.Category)
|
||||
export class TDocumentCategory extends TDoc implements DocumentCategory {
|
||||
@Prop(TypeRef(core.class.TypedSpace), core.string.Space)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare space: Ref<TypedSpace>
|
||||
|
||||
@Prop(TypeString(), documents.string.Code)
|
||||
@Index(IndexKind.FullText)
|
||||
code!: string
|
||||
|
||||
@Prop(TypeString(), documents.string.Title)
|
||||
@Index(IndexKind.FullText)
|
||||
title!: string
|
||||
|
||||
@Prop(TypeMarkup(), documents.string.Description)
|
||||
@Index(IndexKind.FullText)
|
||||
description?: string
|
||||
|
||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
|
||||
attachments?: CollectionSize<Attachment>
|
||||
}
|
||||
|
||||
@Model(documents.class.ControlledDocument, documents.class.HierarchyDocument)
|
||||
@UX(
|
||||
documents.string.ControlledDocument,
|
||||
documents.icon.Document,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
documents.string.Documents
|
||||
)
|
||||
export class TControlledDocument extends THierarchyDocument implements ControlledDocument {
|
||||
@Prop(Collection(documents.class.DocumentRequest), documents.string.Requests)
|
||||
@Hidden()
|
||||
requests!: CollectionSize<DocumentRequest>
|
||||
|
||||
@Prop(ArrOf(TypeRef(contact.mixin.Employee)), documents.string.Reviewers)
|
||||
reviewers!: Ref<Employee>[]
|
||||
|
||||
@Prop(ArrOf(TypeRef(contact.mixin.Employee)), documents.string.Approvers)
|
||||
approvers!: Ref<Employee>[]
|
||||
|
||||
@Prop(ArrOf(TypeRef(contact.mixin.Employee)), documents.string.CoAuthors)
|
||||
coAuthors!: Ref<Employee>[]
|
||||
|
||||
@Prop(TypeNumber(), documents.string.ReviewInterval)
|
||||
reviewInterval?: number
|
||||
|
||||
@Prop(TypeControlledDocumentState(), documents.string.ControlledStatus)
|
||||
controlledState?: ControlledDocumentState
|
||||
|
||||
@Prop(TypeDate(DateRangeMode.DATE), documents.string.EffectiveDate)
|
||||
effectiveDate?: Timestamp
|
||||
|
||||
@Prop(TypeDate(DateRangeMode.DATE), documents.string.PlannedEffectiveDate)
|
||||
@Hidden()
|
||||
plannedEffectiveDate!: Timestamp
|
||||
|
||||
@Prop(TypeRef(documents.class.Document), documents.string.ChangeControl)
|
||||
@Hidden()
|
||||
changeControl!: Ref<ChangeControl>
|
||||
}
|
||||
|
||||
@Model(documents.class.ChangeControl, core.class.Doc, DOMAIN_DOCUMENTS)
|
||||
@UX(documents.string.ChangeControl)
|
||||
export class TChangeControl extends TDoc implements ChangeControl {
|
||||
@Prop(TypeString(), documents.string.Description)
|
||||
@Index(IndexKind.FullText)
|
||||
description!: string
|
||||
|
||||
@Prop(TypeString(), documents.string.Reason)
|
||||
@Index(IndexKind.FullText)
|
||||
reason!: string
|
||||
|
||||
@Prop(TypeString(), documents.string.ImpactAnalysis)
|
||||
impact!: string
|
||||
|
||||
@Prop(ArrOf(TypeRef(documents.class.Document)), documents.string.ImpactedDocuments)
|
||||
impactedDocuments!: Ref<Document>[]
|
||||
}
|
||||
|
||||
@Model(documents.class.DocumentSection, core.class.AttachedDoc, DOMAIN_DOCUMENTS)
|
||||
@UX(documents.string.Section)
|
||||
export class TDocumentSection extends TAttachedDoc implements DocumentSection {
|
||||
@Prop(TypeString(), documents.string.Title)
|
||||
@Index(IndexKind.FullText)
|
||||
title!: string
|
||||
|
||||
@Prop(TypeString(), documents.string.Rank)
|
||||
@Hidden()
|
||||
rank!: string
|
||||
|
||||
@Prop(TypeString(), documents.string.Key)
|
||||
@Hidden()
|
||||
key!: string
|
||||
|
||||
@Prop(TypeRef(documents.mixin.DocumentTemplateSection), documents.string.SectionTemplate)
|
||||
templateSectionId?: Ref<DocumentTemplateSection>
|
||||
}
|
||||
|
||||
@Model(documents.class.DocumentSnapshot, core.class.AttachedDoc, DOMAIN_DOCUMENTS)
|
||||
@UX(documents.string.Snapshot)
|
||||
export class TDocumentSnapshot extends TAttachedDoc implements DocumentSnapshot {
|
||||
@Prop(TypeString(), documents.string.Name)
|
||||
name?: string
|
||||
|
||||
@Prop(TypeCollaborativeDoc(), documents.string.CollaborativeDocument)
|
||||
@Hidden()
|
||||
content!: CollaborativeDoc
|
||||
|
||||
@Prop(TypeDocumentState(), documents.string.Status)
|
||||
state?: DocumentState
|
||||
|
||||
@Prop(Collection(documents.class.DocumentSection), documents.string.Sections)
|
||||
sections!: CollectionSize<DocumentSection>
|
||||
}
|
||||
|
||||
@Model(documents.class.ControlledDocumentSnapshot, documents.class.DocumentSnapshot)
|
||||
@UX(documents.string.ControlledSnapshot)
|
||||
export class TControlledDocumentSnapshot extends TDocumentSnapshot implements ControlledDocumentSnapshot {
|
||||
@Prop(TypeControlledDocumentState(), documents.string.Status)
|
||||
controlledState!: ControlledDocumentState
|
||||
}
|
||||
|
||||
@Model(documents.class.DocumentComment, chunter.class.ChatMessage)
|
||||
export class TDocumentComment extends TChatMessage implements DocumentComment {
|
||||
@Prop(TypeString(), documents.string.SectionKey)
|
||||
sectionKey?: string
|
||||
|
||||
@Prop(TypeString(), documents.string.ID)
|
||||
nodeId?: string
|
||||
|
||||
@Prop(TypeBoolean(), documents.string.Resolve)
|
||||
resolved?: boolean
|
||||
|
||||
@Prop(TypeNumber(), documents.string.Index)
|
||||
index?: number
|
||||
}
|
||||
|
||||
@Mixin(documents.mixin.DocumentTemplateSection, documents.class.DocumentSection)
|
||||
@UX(documents.string.SectionTemplate)
|
||||
export class TDocumentTemplateSection extends TDocumentSection implements DocumentTemplateSection {
|
||||
@Prop(TypeBoolean(), documents.string.Required)
|
||||
mandatory?: boolean
|
||||
|
||||
@Prop(TypeString(), documents.string.Description)
|
||||
@Index(IndexKind.FullText)
|
||||
description?: string
|
||||
|
||||
@Prop(TypeMarkup(), documents.string.Guidance)
|
||||
@Index(IndexKind.FullText)
|
||||
guidance?: Markup
|
||||
}
|
||||
|
||||
@Model(documents.class.CollaborativeDocumentSection, documents.class.DocumentSection)
|
||||
@UX(documents.string.CollaborativeSection)
|
||||
export class TCollaborativeDocumentSection extends TDocumentSection implements CollaborativeDocumentSection {
|
||||
@Prop(TypeString(), documents.string.CollaboratorSectionId)
|
||||
@Hidden()
|
||||
collaboratorSectionId!: string
|
||||
|
||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
|
||||
attachments?: CollectionSize<Attachment>
|
||||
}
|
||||
|
||||
@Model(documents.class.AttachmentsDocumentSection, documents.class.DocumentSection)
|
||||
@UX(documents.string.AttachmentsSection)
|
||||
export class TAttachmentsDocumentSection extends TDocumentSection implements AttachmentsDocumentSection {
|
||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
|
||||
attachments?: CollectionSize<Attachment>
|
||||
|
||||
@Prop(TypeNumber(), documents.string.AttachmentsMax)
|
||||
maximum?: number
|
||||
}
|
||||
|
||||
@Model(documents.class.Sequence, core.class.Doc, DOMAIN_DOCUMENTS)
|
||||
export class TSequence extends TDoc implements Sequence {
|
||||
attachedTo!: Ref<Class<Doc>>
|
||||
sequence!: number
|
||||
}
|
||||
|
||||
@Model(documents.class.DocumentRequest, request.class.Request)
|
||||
@UX(documents.string.DocumentRequest)
|
||||
export class TDocumentRequest extends TRequest implements DocumentRequest {}
|
||||
|
||||
@Model(documents.class.DocumentReviewRequest, documents.class.DocumentRequest)
|
||||
@UX(documents.string.DocumentReviewRequest)
|
||||
export class TDocumentReviewRequest extends TDocumentRequest implements DocumentReviewRequest {}
|
||||
|
||||
@Model(documents.class.DocumentApprovalRequest, documents.class.DocumentRequest)
|
||||
@UX(documents.string.DocumentApprovalRequest)
|
||||
export class TDocumentApprovalRequest extends TDocumentRequest implements DocumentApprovalRequest {}
|
||||
|
||||
@Mixin(documents.mixin.DocumentSectionEditor, core.class.Class)
|
||||
export class TDocumentSectionEditor extends TClass implements DocumentSectionEditor {
|
||||
editor!: AnyComponent
|
||||
}
|
||||
|
||||
@Mixin(documents.mixin.DocumentSectionPresenter, core.class.Class)
|
||||
export class TDocumentSectionPresenter extends TClass implements DocumentSectionPresenter {
|
||||
presenter!: AnyComponent
|
||||
}
|
||||
|
||||
@Mixin(documents.mixin.DocumentSectionCreator, core.class.Class)
|
||||
export class TDocumentSectionCreator extends TClass implements DocumentSectionCreator {
|
||||
creator!: Resource<
|
||||
(
|
||||
document: Document,
|
||||
section: AttachedData<DocumentSection>,
|
||||
copyFrom?: DocumentSection
|
||||
) => AttachedData<DocumentSection>
|
||||
>
|
||||
}
|
||||
|
||||
@Mixin(documents.mixin.DocumentSpaceTypeData, documents.class.DocumentSpace)
|
||||
@UX(getEmbeddedLabel('Default Documents'), documents.icon.Document)
|
||||
export class TDocumentSpaceTypeData extends TDocumentSpace implements RolesAssignment {
|
||||
[key: Ref<Role>]: Ref<Account>[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function TypeDocumentState (): Type<DocumentState> {
|
||||
return { _class: documents.class.TypeDocumentState, label: documents.string.Status }
|
||||
}
|
||||
|
||||
@UX(documents.string.Status)
|
||||
@Model(documents.class.TypeDocumentState, core.class.Type)
|
||||
export class TTypeDocumentState extends TType {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function TypeControlledDocumentState (): Type<ControlledDocumentState> {
|
||||
return { _class: documents.class.TypeControlledDocumentState, label: documents.string.ControlledStatus }
|
||||
}
|
||||
|
||||
@UX(documents.string.ControlledStatus)
|
||||
@Model(documents.class.TypeControlledDocumentState, core.class.Type)
|
||||
export class TTypeControlledDocumentState extends TType {}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig",
|
||||
"rigProfile": "model"
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@hcengineering/model-print",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"svelte": "src/index.ts",
|
||||
"types": "types/index.d.ts",
|
||||
"author": "Uberflow Contributors",
|
||||
"template": "@hcengineering/model-package",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "compile",
|
||||
"build:watch": "compile",
|
||||
"format": "format src",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint": "^8.54.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"prettier": "^3.1.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/core": "^0.6.28",
|
||||
"@hcengineering/model-core": "^0.6.0",
|
||||
"@hcengineering/model": "^0.6.7",
|
||||
"@hcengineering/model-view": "^0.6.0",
|
||||
"@hcengineering/platform": "^0.6.9",
|
||||
"@hcengineering/view": "^0.6.9",
|
||||
"@hcengineering/ui": "^0.6.11",
|
||||
"@hcengineering/presentation": "^0.6.2",
|
||||
"@hcengineering/model-presentation": "^0.6.0",
|
||||
"@hcengineering/print-resources": "^0.6.0",
|
||||
"@hcengineering/print": "^0.6.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/model-core'
|
||||
import view, { createAction } from '@hcengineering/model-view'
|
||||
import presentation from '@hcengineering/model-presentation'
|
||||
|
||||
import print from './plugin'
|
||||
|
||||
export { printId } from '@hcengineering/print'
|
||||
export * from './migration'
|
||||
export default print
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: print.actionImpl.Print,
|
||||
label: print.string.PrintToPDF,
|
||||
icon: print.icon.Print,
|
||||
category: view.category.General,
|
||||
input: 'focus', // NOTE: should only work for one doc for now, not bulk
|
||||
target: core.class.Doc,
|
||||
context: { mode: ['context', 'browser'], group: 'tools' },
|
||||
visibilityTester: print.function.CanPrint
|
||||
},
|
||||
print.action.Print
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
presentation.class.FilePreviewExtension,
|
||||
core.space.Model,
|
||||
{
|
||||
contentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
alignment: 'float',
|
||||
component: print.component.DOCXViewer,
|
||||
extension: presentation.extension.FilePreviewExtension,
|
||||
availabilityChecker: print.function.CanConvert
|
||||
},
|
||||
print.previewExtension.DOCX
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Copyright © 2024 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.
|
||||
//
|
||||
|
||||
import { type MigrateOperation, type MigrationClient, type MigrationUpgradeClient } from '@hcengineering/model'
|
||||
|
||||
export const loveOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Copyright © 2024 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.
|
||||
//
|
||||
|
||||
import { type Doc, type Ref } from '@hcengineering/core'
|
||||
import { mergeIds } from '@hcengineering/platform'
|
||||
import { type ViewAction, type Action } from '@hcengineering/view'
|
||||
import { type FilePreviewExtension } from '@hcengineering/presentation'
|
||||
import { printId } from '@hcengineering/print'
|
||||
// This import is needed because of similar import in print plugin
|
||||
// Otherwise, TS complains about missing types for resulting merge
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import { type AnyComponent } from '@hcengineering/ui/src/types'
|
||||
import print from '@hcengineering/print-resources/src/plugin'
|
||||
|
||||
export default mergeIds(printId, print, {
|
||||
action: {
|
||||
Print: '' as Ref<Action<Doc, Record<string, any>>>
|
||||
},
|
||||
actionImpl: {
|
||||
Print: '' as ViewAction
|
||||
},
|
||||
previewExtension: {
|
||||
DOCX: '' as Ref<FilePreviewExtension>
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig",
|
||||
"rigProfile": "model"
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"name": "@hcengineering/model-products",
|
||||
"version": "0.1.0",
|
||||
"main": "lib/index.js",
|
||||
"svelte": "src/index.ts",
|
||||
"types": "types/index.d.ts",
|
||||
"author": "Hardcore Engineering Inc.",
|
||||
"template": "@hcengineering/model-package",
|
||||
"scripts": {
|
||||
"build": "compile",
|
||||
"build:watch": "compile",
|
||||
"genmodel": "ts-node src/__genmodel.ts",
|
||||
"version": "ts-node src/__showversion.ts",
|
||||
"bumpversion": "ts-node src/__bumpversion.ts",
|
||||
"format": "format src",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"@types/node": "~20.11.16",
|
||||
"ts-node": "^10.8.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"prettier": "^3.1.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/attachment": "^0.6.9",
|
||||
"@hcengineering/model": "^0.6.7",
|
||||
"@hcengineering/model-attachment": "^0.6.0",
|
||||
"@hcengineering/model-core": "^0.6.0",
|
||||
"@hcengineering/model-presentation": "^0.6.0",
|
||||
"@hcengineering/model-view": "^0.6.0",
|
||||
"@hcengineering/model-workbench": "^0.6.1",
|
||||
"@hcengineering/activity": "^0.6.0",
|
||||
"@hcengineering/platform": "^0.6.9",
|
||||
"@hcengineering/core": "^0.6.28",
|
||||
"@hcengineering/contact": "^0.6.20",
|
||||
"@hcengineering/ui": "^0.6.11",
|
||||
"@hcengineering/view": "^0.6.9",
|
||||
"@hcengineering/model-tracker": "^0.6.0",
|
||||
"@hcengineering/chunter": "^0.6.12",
|
||||
"@hcengineering/setting": "^0.6.11",
|
||||
"@hcengineering/products": "~0.1.0",
|
||||
"@hcengineering/products-resources": "~0.1.0",
|
||||
"@hcengineering/controlled-documents": "^0.1.0",
|
||||
"@hcengineering/model-controlled-documents": "^0.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,489 @@
|
||||
//
|
||||
// Copyright © 2024 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.
|
||||
//
|
||||
|
||||
import documents, { TExternalSpace, TProject } from '@hcengineering/model-controlled-documents'
|
||||
import type { Document } from '@hcengineering/controlled-documents'
|
||||
import type { Product, ProductVersionState, ProductVersion } from '@hcengineering/products'
|
||||
import { productsId } from '@hcengineering/products'
|
||||
import activity from '@hcengineering/activity'
|
||||
import { type Attachment } from '@hcengineering/attachment'
|
||||
import contact from '@hcengineering/contact'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import { getRoleAttributeProps } from '@hcengineering/setting'
|
||||
import type { Type, CollectionSize, Markup, Arr, RolesAssignment, Permission, Role } from '@hcengineering/core'
|
||||
import { IndexKind, Ref, Account } from '@hcengineering/core'
|
||||
import {
|
||||
type Builder,
|
||||
Model,
|
||||
Prop,
|
||||
TypeRef,
|
||||
UX,
|
||||
TypeMarkup,
|
||||
Index,
|
||||
TypeString,
|
||||
Hidden,
|
||||
TypeNumber,
|
||||
Collection,
|
||||
ArrOf,
|
||||
TypeAny,
|
||||
ReadOnly,
|
||||
Mixin
|
||||
} from '@hcengineering/model'
|
||||
import attachment from '@hcengineering/model-attachment'
|
||||
import core, { TType } from '@hcengineering/model-core'
|
||||
import presentation from '@hcengineering/model-presentation'
|
||||
import tracker from '@hcengineering/model-tracker'
|
||||
import { type Action, type ViewAction } from '@hcengineering/view'
|
||||
import view, { createAction } from '@hcengineering/model-view'
|
||||
import workbench from '@hcengineering/model-workbench'
|
||||
import { getEmbeddedLabel, type Asset } from '@hcengineering/platform'
|
||||
|
||||
import products from './plugin'
|
||||
import { roles } from './roles'
|
||||
|
||||
export { productsId } from '@hcengineering/products/src/index'
|
||||
|
||||
const productPermissions: Ref<Permission>[] = [
|
||||
documents.permission.CreateDocument,
|
||||
documents.permission.ReviewDocument,
|
||||
documents.permission.ApproveDocument,
|
||||
documents.permission.CoAuthorDocument,
|
||||
documents.permission.UpdateDocumentOwner,
|
||||
core.permission.UpdateSpace,
|
||||
core.permission.ArchiveSpace
|
||||
]
|
||||
|
||||
/** @public */
|
||||
export function TypeProductVersionState (): Type<ProductVersionState> {
|
||||
return { _class: products.class.TypeProductVersionState, label: products.string.ProductVersion }
|
||||
}
|
||||
|
||||
@Model(products.class.TypeProductVersionState, core.class.Type)
|
||||
@UX(products.string.ProductVersionState)
|
||||
export class TTypeProductVersionState extends TType {}
|
||||
|
||||
@Model(products.class.Product, documents.class.ExternalSpace)
|
||||
@UX(products.string.Product, products.icon.Product, 'Product', 'name', undefined, products.string.Products)
|
||||
export class TProduct extends TExternalSpace implements Product {
|
||||
@Prop(ArrOf(TypeRef(core.class.Account)), core.string.Members)
|
||||
declare members: Arr<Ref<Account>>
|
||||
|
||||
@Prop(TypeMarkup(), products.string.Description)
|
||||
@Index(IndexKind.FullText)
|
||||
fullDescription?: string
|
||||
|
||||
@Prop(TypeString(), products.string.Icon)
|
||||
@Index(IndexKind.FullText)
|
||||
@Hidden()
|
||||
icon?: Asset
|
||||
|
||||
@Prop(TypeNumber(), products.string.Color)
|
||||
@Index(IndexKind.FullText)
|
||||
@Hidden()
|
||||
color?: number
|
||||
|
||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
|
||||
attachments?: CollectionSize<Attachment>
|
||||
}
|
||||
|
||||
@Model(products.class.ProductVersion, documents.class.Project)
|
||||
@UX(products.string.ProductVersion, products.icon.ProductVersion, undefined, undefined, products.string.ProductVersions)
|
||||
export class TProductVersion extends TProject implements ProductVersion {
|
||||
@Prop(TypeRef(products.class.Product), products.string.Product)
|
||||
@Index(IndexKind.Indexed)
|
||||
declare space: Ref<Product>
|
||||
|
||||
@Prop(TypeNumber(), products.string.Major)
|
||||
@ReadOnly()
|
||||
major!: number
|
||||
|
||||
@Prop(TypeNumber(), products.string.Minor)
|
||||
@ReadOnly()
|
||||
minor!: number
|
||||
|
||||
@Prop(TypeString(), products.string.Codename)
|
||||
codename?: string
|
||||
|
||||
@Prop(TypeMarkup(), products.string.Description)
|
||||
@Index(IndexKind.FullText)
|
||||
description!: Markup
|
||||
|
||||
@Prop(TypeRef(products.class.ProductVersion), products.string.ProductVersionParent)
|
||||
@Index(IndexKind.Indexed)
|
||||
@ReadOnly()
|
||||
parent!: Ref<ProductVersion>
|
||||
|
||||
@Prop(TypeProductVersionState(), products.string.ProductVersionState)
|
||||
@ReadOnly()
|
||||
state!: ProductVersionState
|
||||
|
||||
@Prop(
|
||||
TypeAny(
|
||||
products.component.ChangeControlInlineEditor,
|
||||
products.string.ChangeControl,
|
||||
products.component.ChangeControlInlineEditor
|
||||
),
|
||||
products.string.ChangeControl
|
||||
)
|
||||
@ReadOnly()
|
||||
changeControl?: Ref<Document>
|
||||
}
|
||||
|
||||
@Mixin(products.mixin.ProductTypeData, products.class.Product)
|
||||
@UX(getEmbeddedLabel('Default Products'), products.icon.ProductVersion)
|
||||
export class TProductTypeData extends TProduct implements RolesAssignment {
|
||||
[key: Ref<Role>]: Ref<Account>[]
|
||||
}
|
||||
|
||||
function defineProduct (builder: Builder): void {
|
||||
builder.createModel(TProduct, TProductTypeData)
|
||||
|
||||
builder.mixin(products.class.Product, core.class.Class, activity.mixin.ActivityDoc, {})
|
||||
|
||||
builder.mixin(products.class.Product, core.class.Class, view.mixin.ObjectIdentifier, {
|
||||
provider: products.function.ProductIdentifierProvider
|
||||
})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: products.class.Product,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
})
|
||||
|
||||
builder.mixin(products.class.Product, core.class.Class, view.mixin.ObjectEditor, {
|
||||
editor: products.component.EditProduct
|
||||
})
|
||||
|
||||
builder.mixin(products.class.Product, core.class.Class, view.mixin.ObjectPanel, {
|
||||
component: products.component.EditProduct
|
||||
})
|
||||
|
||||
builder.mixin(products.class.Product, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: products.component.ProductPresenter
|
||||
})
|
||||
|
||||
builder.mixin(products.class.Product, core.class.Class, view.mixin.SpacePresenter, {
|
||||
presenter: documents.component.DocumentSpacePresenter
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
presentation.class.ObjectSearchCategory,
|
||||
core.space.Model,
|
||||
{
|
||||
title: products.string.Products,
|
||||
icon: products.icon.Product,
|
||||
label: products.string.SearchProduct,
|
||||
query: products.completion.ProductQuery,
|
||||
context: ['search', 'mention', 'spotlight'],
|
||||
classToSearch: products.class.Product
|
||||
},
|
||||
products.completion.ProductQueryCategory
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
attachTo: products.class.Product,
|
||||
descriptor: view.viewlet.Table,
|
||||
config: [
|
||||
'',
|
||||
'archived',
|
||||
{
|
||||
key: 'owners',
|
||||
presenter: contact.component.AccountArrayEditor,
|
||||
label: core.string.Owners,
|
||||
props: {
|
||||
readonly: true,
|
||||
size: 'card'
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'members',
|
||||
presenter: contact.component.AccountArrayEditor,
|
||||
label: core.string.Members,
|
||||
props: {
|
||||
readonly: true,
|
||||
size: 'card'
|
||||
}
|
||||
},
|
||||
'modifiedOn'
|
||||
],
|
||||
configOptions: {
|
||||
hiddenKeys: ['name'],
|
||||
sortable: true,
|
||||
strict: true
|
||||
}
|
||||
},
|
||||
products.viewlet.TableProduct
|
||||
)
|
||||
|
||||
builder.mixin(products.class.Product, core.class.Class, view.mixin.ClassFilters, {
|
||||
filters: ['archived', 'private', 'createdBy'],
|
||||
ignoreKeys: ['name', 'type', 'description', 'fullDescription', 'modifiedBy', 'createdOn', 'modifiedOn'],
|
||||
getVisibleFilters: products.function.GetVisibleFilters
|
||||
})
|
||||
|
||||
builder.mixin(products.class.Product, core.class.Class, view.mixin.IgnoreActions, {
|
||||
actions: [tracker.action.NewRelatedIssue]
|
||||
})
|
||||
|
||||
createAction(builder, {
|
||||
label: products.string.Unarchive,
|
||||
icon: view.icon.Archive,
|
||||
action: view.actionImpl.UpdateDocument as ViewAction,
|
||||
actionProps: {
|
||||
key: 'archived',
|
||||
ask: true,
|
||||
value: false,
|
||||
label: products.string.Unarchive,
|
||||
message: products.string.UnarchiveConfirm
|
||||
},
|
||||
input: 'any',
|
||||
category: view.category.General,
|
||||
target: products.class.Product,
|
||||
visibilityTester: view.function.CanArchiveSpace,
|
||||
query: {
|
||||
archived: true
|
||||
},
|
||||
context: {
|
||||
mode: ['context', 'browser'],
|
||||
group: 'tools'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function defineSpaceType (builder: Builder): void {
|
||||
for (const role of roles) {
|
||||
const { label, roleType } = getRoleAttributeProps(role.name)
|
||||
|
||||
Prop(roleType, label)(TProductTypeData.prototype, role._id)
|
||||
}
|
||||
|
||||
builder.createDoc(
|
||||
documents.class.DocumentSpaceTypeDescriptor,
|
||||
core.space.Model,
|
||||
{
|
||||
name: products.string.ProductsApplication,
|
||||
description: products.string.ProductsApplication,
|
||||
icon: products.icon.ProductsApplication,
|
||||
baseClass: products.class.Product,
|
||||
availablePermissions: [...productPermissions],
|
||||
projectClass: products.class.ProductVersion
|
||||
},
|
||||
products.spaceTypeDescriptor.ProductType
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
documents.class.DocumentSpaceType,
|
||||
core.space.Model,
|
||||
{
|
||||
name: 'Default Products',
|
||||
descriptor: products.spaceTypeDescriptor.ProductType,
|
||||
roles: roles.length,
|
||||
projects: true,
|
||||
targetClass: products.mixin.ProductTypeData
|
||||
},
|
||||
products.spaceType.ProductType
|
||||
)
|
||||
|
||||
for (const role of roles) {
|
||||
builder.createDoc(
|
||||
core.class.Role,
|
||||
core.space.Model,
|
||||
{
|
||||
attachedTo: products.spaceType.ProductType,
|
||||
attachedToClass: documents.class.DocumentSpaceType,
|
||||
collection: 'roles',
|
||||
name: role.name,
|
||||
permissions: role.permissions
|
||||
},
|
||||
role._id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function defineProductVersion (builder: Builder): void {
|
||||
builder.createModel(TProductVersion)
|
||||
|
||||
builder.mixin(products.class.ProductVersion, core.class.Class, activity.mixin.ActivityDoc, {})
|
||||
|
||||
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
||||
ofClass: products.class.ProductVersion,
|
||||
components: { input: chunter.component.ChatMessageInput }
|
||||
})
|
||||
|
||||
builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.ObjectEditor, {
|
||||
editor: products.component.EditProductVersion
|
||||
})
|
||||
|
||||
builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.ObjectPanel, {
|
||||
component: products.component.EditProductVersion
|
||||
})
|
||||
|
||||
builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: products.component.ProductVersionPresenter
|
||||
})
|
||||
|
||||
builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.CollectionPresenter, {
|
||||
presenter: products.component.ProductVersionsPresenter
|
||||
})
|
||||
|
||||
builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.CollectionEditor, {
|
||||
editor: products.component.ProductVersionsEditor
|
||||
})
|
||||
|
||||
builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.AttributeEditor, {
|
||||
inlineEditor: products.component.ProductVersionInlineEditor
|
||||
})
|
||||
|
||||
// builder.createDoc(
|
||||
// presentation.class.ObjectSearchCategory,
|
||||
// core.space.Model,
|
||||
// {
|
||||
// title: products.string.ProductVersions,
|
||||
// icon: products.icon.ProductVersion,
|
||||
// label: products.string.SearchProductVersion,
|
||||
// query: products.completion.ProductVersionQuery,
|
||||
// context: ['search', 'mention', 'spotlight'],
|
||||
// classToSearch: products.class.ProductVersion
|
||||
// },
|
||||
// products.completion.ProductVersionQueryCategory
|
||||
// )
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
attachTo: products.class.ProductVersion,
|
||||
descriptor: view.viewlet.Table,
|
||||
config: [
|
||||
'',
|
||||
'space',
|
||||
'state',
|
||||
'$lookup.parent',
|
||||
{
|
||||
key: '$lookup.space.owners',
|
||||
presenter: contact.component.AccountArrayEditor,
|
||||
label: core.string.Owners,
|
||||
props: {
|
||||
readonly: true,
|
||||
size: 'card'
|
||||
}
|
||||
},
|
||||
'createdBy',
|
||||
'createdOn'
|
||||
],
|
||||
configOptions: {
|
||||
hiddenKeys: ['attachedTo', 'description', 'readonly'],
|
||||
sortable: true
|
||||
}
|
||||
},
|
||||
products.viewlet.TableProductVersion
|
||||
)
|
||||
|
||||
builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.ClassFilters, {
|
||||
filters: ['space', 'name', 'parent', 'state'],
|
||||
ignoreKeys: ['createdBy', 'modifiedBy', 'createdOn', 'modifiedOn']
|
||||
})
|
||||
|
||||
builder.mixin(products.class.ProductVersion, core.class.Class, view.mixin.IgnoreActions, {
|
||||
actions: [tracker.action.NewRelatedIssue, documents.action.CreateDocument as Ref<Action>]
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: view.actionImpl.Delete,
|
||||
label: view.string.Delete,
|
||||
icon: view.icon.Delete,
|
||||
visibilityTester: products.function.CanDeleteProductVersion,
|
||||
category: view.category.General,
|
||||
input: 'any',
|
||||
target: products.class.ProductVersion,
|
||||
context: { mode: ['context', 'browser'], group: 'remove' },
|
||||
override: [view.action.Delete]
|
||||
},
|
||||
products.action.DeleteProductVersion
|
||||
)
|
||||
}
|
||||
|
||||
function defineProductVersionState (builder: Builder): void {
|
||||
builder.createModel(TTypeProductVersionState)
|
||||
|
||||
builder.mixin(products.class.TypeProductVersionState, core.class.Class, view.mixin.AttributeEditor, {
|
||||
inlineEditor: products.component.ProductVersionStateEditor
|
||||
})
|
||||
|
||||
builder.mixin(products.class.TypeProductVersionState, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: products.component.ProductVersionStatePresenter
|
||||
})
|
||||
|
||||
builder.mixin(products.class.TypeProductVersionState, core.class.Class, view.mixin.AttributeFilter, {
|
||||
component: view.component.ValueFilter
|
||||
})
|
||||
}
|
||||
|
||||
function defineApplication (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
workbench.class.Application,
|
||||
core.space.Model,
|
||||
{
|
||||
label: products.string.ProductsApplication,
|
||||
icon: products.icon.ProductsApplication,
|
||||
alias: productsId,
|
||||
hidden: false,
|
||||
navigatorModel: {
|
||||
spaces: [],
|
||||
specials: [
|
||||
{
|
||||
id: 'products',
|
||||
label: products.string.Products,
|
||||
icon: products.icon.Product,
|
||||
component: workbench.component.SpecialView,
|
||||
componentProps: {
|
||||
_class: products.class.Product,
|
||||
label: products.string.Products
|
||||
},
|
||||
position: 'top'
|
||||
},
|
||||
{
|
||||
id: 'product-versions',
|
||||
label: products.string.ProductVersions,
|
||||
icon: products.icon.ProductVersion,
|
||||
component: workbench.component.SpecialView,
|
||||
componentProps: {
|
||||
_class: products.class.ProductVersion,
|
||||
label: products.string.ProductVersions
|
||||
},
|
||||
position: 'top'
|
||||
}
|
||||
]
|
||||
},
|
||||
navHeaderComponent: products.component.NewProductHeader
|
||||
},
|
||||
products.app.Products
|
||||
)
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
defineSpaceType(builder)
|
||||
defineProduct(builder)
|
||||
defineProductVersion(builder)
|
||||
defineProductVersionState(builder)
|
||||
defineApplication(builder)
|
||||
}
|
||||
|
||||
export { productsOperation } from './migration'
|
||||
export { default } from './plugin'
|
||||
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// Copyright © 2024 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.
|
||||
//
|
||||
|
||||
import { type Employee } from '@hcengineering/contact'
|
||||
import { type Ref, DOMAIN_TX } from '@hcengineering/core'
|
||||
import { type MigrateOperation, type MigrationClient, type MigrationUpgradeClient } from '@hcengineering/model'
|
||||
import core, { DOMAIN_SPACE } from '@hcengineering/model-core'
|
||||
import { type Product } from '@hcengineering/products'
|
||||
|
||||
import products from './plugin'
|
||||
|
||||
async function migrateProductSpacesMixins (client: MigrationClient): Promise<void> {
|
||||
const oldSpaceTypeMixin = `${products.spaceType.ProductType}:type:mixin`
|
||||
const newSpaceTypeMixin = products.mixin.ProductTypeData
|
||||
|
||||
await client.update(
|
||||
DOMAIN_TX,
|
||||
{
|
||||
objectClass: core.class.Attribute,
|
||||
'attributes.attributeOf': oldSpaceTypeMixin
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
'attributes.attributeOf': newSpaceTypeMixin
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
await client.update(
|
||||
DOMAIN_SPACE,
|
||||
{
|
||||
_class: products.class.Product,
|
||||
[oldSpaceTypeMixin]: { $exists: true }
|
||||
},
|
||||
{
|
||||
$rename: {
|
||||
[oldSpaceTypeMixin]: newSpaceTypeMixin
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function migrateProductOwner (client: MigrationClient): Promise<void> {
|
||||
type ExProduct = Product & { owner: Ref<Employee> }
|
||||
|
||||
const docs = await client.find<ExProduct>(DOMAIN_SPACE, { _class: products.class.Product, owner: { $exists: true } })
|
||||
for (const doc of docs) {
|
||||
if (doc.owner == null) continue
|
||||
const currentOwners = doc.owners ?? []
|
||||
const currenMembers = doc.members ?? []
|
||||
|
||||
const owners = Array.from(new Set(...currentOwners, doc.owner))
|
||||
const members = Array.from(new Set(...currenMembers, ...owners))
|
||||
|
||||
await client.update(
|
||||
DOMAIN_SPACE,
|
||||
{ _id: doc._id },
|
||||
{
|
||||
$set: { owners, members },
|
||||
$unset: { owner: undefined }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const productsOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {
|
||||
await migrateProductSpacesMixins(client)
|
||||
await migrateProductOwner(client)
|
||||
},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// Copyright © 2024 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.
|
||||
//
|
||||
|
||||
import '@hcengineering/controlled-documents'
|
||||
import { productsId } from '@hcengineering/products'
|
||||
import products from '@hcengineering/products-resources/src/plugin'
|
||||
import type { Client, Doc, Ref, Role } from '@hcengineering/core'
|
||||
import { type Resource, mergeIds } from '@hcengineering/platform'
|
||||
import { type AnyComponent } from '@hcengineering/ui/src/types'
|
||||
import type { Action } from '@hcengineering/view'
|
||||
|
||||
export default mergeIds(productsId, products, {
|
||||
action: {
|
||||
DeleteProductVersion: '' as Ref<Action<Doc, any>>
|
||||
},
|
||||
component: {
|
||||
CreateProduct: '' as AnyComponent,
|
||||
ProductPresenter: '' as AnyComponent,
|
||||
NewProductHeader: '' as AnyComponent,
|
||||
CreateProductVersion: '' as AnyComponent,
|
||||
ChangeControlInlineEditor: '' as AnyComponent,
|
||||
ProductVersionPresenter: '' as AnyComponent,
|
||||
ProductVersionInlineEditor: '' as AnyComponent,
|
||||
ProductVersionsEditor: '' as AnyComponent,
|
||||
ProductVersionsPresenter: '' as AnyComponent,
|
||||
ProductVersionStateEditor: '' as AnyComponent,
|
||||
ProductVersionStatePresenter: '' as AnyComponent,
|
||||
ProductVersionVersionPresenter: '' as AnyComponent
|
||||
},
|
||||
function: {
|
||||
CanDeleteProductVersion: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
|
||||
ProductIdentifierProvider: '' as Resource<<T extends Doc>(client: Client, ref: Ref<T>, doc?: T) => Promise<string>>
|
||||
},
|
||||
role: {
|
||||
QARA: '' as Ref<Role>,
|
||||
Manager: '' as Ref<Role>,
|
||||
QualifiedUser: '' as Ref<Role>
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// Copyright © 2024 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.
|
||||
//
|
||||
|
||||
import core from '@hcengineering/core'
|
||||
import documents from '@hcengineering/controlled-documents'
|
||||
import products from './plugin'
|
||||
|
||||
export const roles = [
|
||||
{
|
||||
_id: products.role.QualifiedUser,
|
||||
name: 'Qualified User',
|
||||
permissions: [
|
||||
documents.permission.CreateDocument,
|
||||
documents.permission.ReviewDocument,
|
||||
documents.permission.ApproveDocument,
|
||||
documents.permission.CoAuthorDocument
|
||||
]
|
||||
},
|
||||
{
|
||||
_id: products.role.Manager,
|
||||
name: 'Manager',
|
||||
permissions: [
|
||||
core.permission.ArchiveSpace,
|
||||
core.permission.UpdateSpace,
|
||||
documents.permission.CreateDocument,
|
||||
documents.permission.ReviewDocument,
|
||||
documents.permission.ApproveDocument,
|
||||
documents.permission.CoAuthorDocument
|
||||
]
|
||||
},
|
||||
{
|
||||
_id: products.role.QARA,
|
||||
name: 'QARA',
|
||||
permissions: [
|
||||
core.permission.ArchiveSpace,
|
||||
core.permission.UpdateSpace,
|
||||
documents.permission.CreateDocument,
|
||||
documents.permission.ReviewDocument,
|
||||
documents.permission.ApproveDocument,
|
||||
documents.permission.CoAuthorDocument,
|
||||
documents.permission.UpdateDocumentOwner
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
|
||||
roots: ["./src"],
|
||||
coverageReporters: ["text-summary", "html"]
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "@hcengineering/model-questions",
|
||||
"version": "0.1.0",
|
||||
"main": "lib/index.js",
|
||||
"svelte": "src/index.ts",
|
||||
"types": "types/index.d.ts",
|
||||
"author": "Hardcore Engineering Inc",
|
||||
"scripts": {
|
||||
"build": "compile",
|
||||
"build:watch": "compile",
|
||||
"format": "format src",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:test": "jest --passWithNoTests --silent",
|
||||
"_phase:format": "format src",
|
||||
"test": "jest --passWithNoTests --silent",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint": "^8.54.0",
|
||||
"prettier": "^3.1.0",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"@types/jest": "^29.5.5",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@hcengineering/questions": "^0.1.0",
|
||||
"@hcengineering/questions-resources": "^0.1.0",
|
||||
"@hcengineering/contact": "^0.6.20",
|
||||
"@hcengineering/core": "^0.6.28",
|
||||
"@hcengineering/model": "^0.6.7",
|
||||
"@hcengineering/model-core": "^0.6.0",
|
||||
"@hcengineering/model-tracker": "^0.6.0",
|
||||
"@hcengineering/model-view": "^0.6.0",
|
||||
"@hcengineering/platform": "^0.6.9",
|
||||
"@hcengineering/view": "^0.6.9"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// Copyright @ 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
|
||||
import type { Answer, Assessment, Percentage, Question, QuestionOption, Rank } from '@hcengineering/questions'
|
||||
import contact, { type Employee } from '@hcengineering/contact'
|
||||
import { type Domain, IndexKind, type Ref, type Timestamp, type Type } from '@hcengineering/core'
|
||||
import { Hidden, Index, Model, Prop, TypeRef, TypeString, TypeTimestamp, UX } from '@hcengineering/model'
|
||||
import core, { TAttachedDoc, TType } from '@hcengineering/model-core'
|
||||
import questions from '../plugin'
|
||||
|
||||
export const DOMAIN_QUESTIONS = 'questions' as Domain
|
||||
|
||||
/** @public */
|
||||
@Model(questions.type.Rank, core.class.Type)
|
||||
@UX(questions.string.TypeRank)
|
||||
export class TTypeRank extends TType {}
|
||||
|
||||
/** @public */
|
||||
export function TypeRank (): Type<Rank> {
|
||||
return { _class: questions.type.Rank, label: questions.string.TypeRank }
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.type.Percentage, core.class.Type)
|
||||
@UX(questions.string.TypePercentage)
|
||||
export class TTypePercentage extends TType {}
|
||||
|
||||
/** @public */
|
||||
export function TypePercentage (): Type<Percentage> {
|
||||
return { _class: questions.type.Percentage, label: questions.string.TypePercentage }
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.type.QuestionOption, core.class.Type)
|
||||
@UX(questions.string.Option)
|
||||
export class TTypeQuestionOption extends TType {}
|
||||
|
||||
/** @public */
|
||||
export function TypeQuestionOption (): Type<QuestionOption> {
|
||||
return { _class: questions.type.QuestionOption, label: questions.string.TypeQuestionOption }
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.Question, core.class.AttachedDoc, DOMAIN_QUESTIONS)
|
||||
export class TQuestion<QuestionData extends NonNullable<any>> extends TAttachedDoc implements Question<QuestionData> {
|
||||
@Prop(TypeRank(), questions.string.Rank)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
rank!: Rank
|
||||
|
||||
@Prop(TypeString(), questions.string.Question)
|
||||
@Index(IndexKind.FullText)
|
||||
title: string = ''
|
||||
|
||||
@Prop(TypeRef(contact.mixin.Employee), questions.string.Owner)
|
||||
@Index(IndexKind.Indexed)
|
||||
owner!: Ref<Employee>
|
||||
|
||||
@Prop(TypeTimestamp(), questions.string.ReleasedDate)
|
||||
@Index(IndexKind.Indexed)
|
||||
releasedOn: Timestamp | null = null
|
||||
|
||||
@Prop(TypeRef(contact.mixin.Employee), questions.string.ReleasedBy)
|
||||
releasedBy: Ref<Employee> | null = null
|
||||
|
||||
// Should be overridden and decorated in child classes
|
||||
questionData!: QuestionData
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.Assessment, questions.class.Question)
|
||||
export class TAssessment<QuestionData extends NonNullable<any>, AssessmentData extends NonNullable<any>>
|
||||
extends TQuestion<QuestionData>
|
||||
implements Assessment<QuestionData, AssessmentData> {
|
||||
// Should be overridden and decorated in child classes
|
||||
assessmentData!: AssessmentData
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.Answer, core.class.AttachedDoc, DOMAIN_QUESTIONS)
|
||||
export class TAnswer<Q extends Question<any>, AnswerData extends NonNullable<any>>
|
||||
extends TAttachedDoc
|
||||
implements Answer<Q, AnswerData> {
|
||||
// Should be overridden and decorated in child classes
|
||||
question!: Ref<Q>
|
||||
|
||||
// Should be overridden and decorated in child classes
|
||||
answerData!: AnswerData
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//
|
||||
// Copyright @ 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
|
||||
export * from './base'
|
||||
export * from './mixin'
|
||||
export * from './questions/MultipleChoice'
|
||||
export * from './questions/Ordering'
|
||||
export * from './questions/SingleChoice'
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Copyright @ 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
|
||||
import type {
|
||||
Answer,
|
||||
AnswerDataAssessFunction,
|
||||
Assessment,
|
||||
Question,
|
||||
QuestionInitFunction,
|
||||
QuestionMixin,
|
||||
QuestionDataPresenter,
|
||||
QuestionDataEditor,
|
||||
AnswerDataPresenter,
|
||||
AnswerDataEditor
|
||||
} from '@hcengineering/questions'
|
||||
import type { Class, Ref } from '@hcengineering/core'
|
||||
import { Mixin } from '@hcengineering/model'
|
||||
import core, { TClass } from '@hcengineering/model-core'
|
||||
import type { Resource } from '@hcengineering/platform'
|
||||
import questions from '../plugin'
|
||||
|
||||
/** @public */
|
||||
@Mixin(questions.mixin.QuestionMixin, core.class.Class)
|
||||
export class TQuestionMixin<Q extends Question<any>, A extends Answer<Q, any>>
|
||||
extends TClass
|
||||
implements QuestionMixin<Q, A> {
|
||||
questionInit!: Resource<QuestionInitFunction<Q>>
|
||||
questionDataPresenter!: Resource<QuestionDataPresenter<Q>>
|
||||
questionDataEditor!: Resource<QuestionDataEditor<Q>>
|
||||
answerClassRef!: Ref<Class<A>>
|
||||
answerDataPresenter!: Resource<AnswerDataPresenter<Q, A>>
|
||||
answerDataEditor!: Resource<AnswerDataEditor<Q, A>>
|
||||
answerDataAssess!: Q extends Assessment<any, any> ? Resource<AnswerDataAssessFunction<Q, A>> : null
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// Copyright @ 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
|
||||
import type {
|
||||
MultipleChoiceAnswerData,
|
||||
MultipleChoiceAssessment,
|
||||
MultipleChoiceAssessmentAnswer,
|
||||
MultipleChoiceAssessmentData,
|
||||
MultipleChoiceQuestion,
|
||||
MultipleChoiceQuestionAnswer,
|
||||
MultipleChoiceQuestionData
|
||||
} from '@hcengineering/questions'
|
||||
import { IndexKind, type Ref, type Type } from '@hcengineering/core'
|
||||
import { Hidden, Index, Model, Prop, TypeRef, UX } from '@hcengineering/model'
|
||||
import core, { TType } from '@hcengineering/model-core'
|
||||
import questions from '../../plugin'
|
||||
import { TAnswer, TAssessment, TQuestion } from '../base'
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.MultipleChoiceQuestionData, core.class.Type)
|
||||
@UX(questions.string.Question)
|
||||
export class TMultipleChoiceQuestionData extends TType {}
|
||||
|
||||
/** @public */
|
||||
export function TypeMultipleChoiceQuestionData (): Type<MultipleChoiceQuestionData> {
|
||||
return {
|
||||
_class: questions.class.MultipleChoiceQuestionData,
|
||||
label: questions.string.MultipleChoiceQuestion
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.MultipleChoiceAssessmentData, core.class.Type)
|
||||
@UX(questions.string.Assessment)
|
||||
export class TMultipleChoiceAssessmentData extends TType {}
|
||||
|
||||
/** @public */
|
||||
export function TypeMultipleChoiceAssessmentData (): Type<MultipleChoiceAssessmentData> {
|
||||
return {
|
||||
_class: questions.class.MultipleChoiceAssessmentData,
|
||||
label: questions.string.MultipleChoiceAssessment
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.MultipleChoiceAnswerData, core.class.Type)
|
||||
@UX(questions.string.Answer)
|
||||
export class TMultipleChoiceAnswerData extends TType {}
|
||||
|
||||
/** @public */
|
||||
export function TypeMultipleChoiceAnswerData (): Type<MultipleChoiceAnswerData> {
|
||||
return {
|
||||
_class: questions.class.MultipleChoiceAnswerData,
|
||||
label: questions.string.MultipleChoice
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.MultipleChoiceQuestion, questions.class.Question)
|
||||
@UX(questions.string.MultipleChoiceQuestion)
|
||||
export class TMultipleChoiceQuestion extends TQuestion<MultipleChoiceQuestionData> implements MultipleChoiceQuestion {
|
||||
@Prop(TypeMultipleChoiceQuestionData(), questions.string.Question)
|
||||
declare questionData: MultipleChoiceQuestionData
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.MultipleChoiceQuestionAnswer, questions.class.Answer)
|
||||
@UX(questions.string.MultipleChoice)
|
||||
export class TMultipleChoiceQuestionAnswer
|
||||
extends TAnswer<MultipleChoiceQuestion, MultipleChoiceAnswerData>
|
||||
implements MultipleChoiceQuestionAnswer {
|
||||
@Prop(TypeRef(questions.class.MultipleChoiceQuestion), questions.string.Question)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare question: Ref<MultipleChoiceQuestion>
|
||||
|
||||
@Prop(TypeMultipleChoiceAnswerData(), questions.string.Answer)
|
||||
declare answerData: MultipleChoiceAnswerData
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.MultipleChoiceAssessment, questions.class.Assessment)
|
||||
@UX(questions.string.MultipleChoiceAssessment)
|
||||
export class TMultipleChoiceAssessment
|
||||
extends TAssessment<MultipleChoiceQuestionData, MultipleChoiceAssessmentData>
|
||||
implements MultipleChoiceAssessment {
|
||||
@Prop(TypeMultipleChoiceAssessmentData(), questions.string.Assessment)
|
||||
declare assessmentData: MultipleChoiceAssessmentData
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.MultipleChoiceAssessmentAnswer, questions.class.Answer)
|
||||
@UX(questions.string.MultipleChoice)
|
||||
export class TMultipleChoiceAssessmentAnswer
|
||||
extends TAnswer<MultipleChoiceAssessment, MultipleChoiceAnswerData>
|
||||
implements MultipleChoiceAssessmentAnswer {
|
||||
@Prop(TypeRef(questions.class.MultipleChoiceAssessment), questions.string.Question)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare question: Ref<MultipleChoiceAssessment>
|
||||
|
||||
@Prop(TypeMultipleChoiceAnswerData(), questions.string.Answer)
|
||||
declare answerData: MultipleChoiceAnswerData
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// Copyright @ 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
|
||||
import type {
|
||||
OrderingAnswerData,
|
||||
OrderingAssessment,
|
||||
OrderingAssessmentAnswer,
|
||||
OrderingAssessmentData,
|
||||
OrderingQuestion,
|
||||
OrderingQuestionAnswer,
|
||||
OrderingQuestionData
|
||||
} from '@hcengineering/questions'
|
||||
import { IndexKind, type Ref, type Type } from '@hcengineering/core'
|
||||
import { Hidden, Index, Model, Prop, TypeRef, UX } from '@hcengineering/model'
|
||||
import core, { TType } from '@hcengineering/model-core'
|
||||
import questions from '../../plugin'
|
||||
import { TAnswer, TAssessment, TQuestion } from '../base'
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.OrderingQuestionData, core.class.Type)
|
||||
@UX(questions.string.Question)
|
||||
export class TOrderingQuestionData extends TType {}
|
||||
|
||||
/** @public */
|
||||
export function TypeOrderingQuestionData (): Type<OrderingQuestionData> {
|
||||
return {
|
||||
_class: questions.class.OrderingQuestionData,
|
||||
label: questions.string.OrderingQuestion
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.OrderingAssessmentData, core.class.Type)
|
||||
@UX(questions.string.Assessment)
|
||||
export class TOrderingAssessmentData extends TType {}
|
||||
|
||||
/** @public */
|
||||
export function TypeOrderingAssessmentData (): Type<OrderingAssessmentData> {
|
||||
return {
|
||||
_class: questions.class.OrderingAssessmentData,
|
||||
label: questions.string.OrderingAssessment
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.OrderingAnswerData, core.class.Type)
|
||||
@UX(questions.string.Answer)
|
||||
export class TOrderingAnswerData extends TType {}
|
||||
|
||||
/** @public */
|
||||
export function TypeOrderingAnswerData (): Type<OrderingAnswerData> {
|
||||
return {
|
||||
_class: questions.class.OrderingAnswerData,
|
||||
label: questions.string.Ordering
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.OrderingQuestion, questions.class.Question)
|
||||
@UX(questions.string.OrderingQuestion)
|
||||
export class TOrderingQuestion extends TQuestion<OrderingQuestionData> implements OrderingQuestion {
|
||||
@Prop(TypeOrderingQuestionData(), questions.string.Question)
|
||||
declare questionData: OrderingQuestionData
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.OrderingQuestionAnswer, questions.class.Answer)
|
||||
@UX(questions.string.Ordering)
|
||||
export class TOrderingQuestionAnswer
|
||||
extends TAnswer<OrderingQuestion, OrderingAnswerData>
|
||||
implements OrderingQuestionAnswer {
|
||||
@Prop(TypeRef(questions.class.OrderingQuestion), questions.string.Question)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare question: Ref<OrderingQuestion>
|
||||
|
||||
@Prop(TypeOrderingAnswerData(), questions.string.Answer)
|
||||
declare answerData: OrderingAnswerData
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.OrderingAssessment, questions.class.Assessment)
|
||||
@UX(questions.string.OrderingAssessment)
|
||||
export class TOrderingAssessment
|
||||
extends TAssessment<OrderingQuestionData, OrderingAssessmentData>
|
||||
implements OrderingAssessment {
|
||||
@Prop(TypeOrderingAssessmentData(), questions.string.Assessment)
|
||||
declare assessmentData: OrderingAssessmentData
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.OrderingAssessmentAnswer, questions.class.Answer)
|
||||
@UX(questions.string.Ordering)
|
||||
export class TOrderingAssessmentAnswer
|
||||
extends TAnswer<OrderingAssessment, OrderingAnswerData>
|
||||
implements OrderingAssessmentAnswer {
|
||||
@Prop(TypeRef(questions.class.OrderingAssessment), questions.string.Question)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare question: Ref<OrderingAssessment>
|
||||
|
||||
@Prop(TypeOrderingAnswerData(), questions.string.Answer)
|
||||
declare answerData: OrderingAnswerData
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// Copyright @ 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
|
||||
import type {
|
||||
SingleChoiceAnswerData,
|
||||
SingleChoiceAssessment,
|
||||
SingleChoiceAssessmentAnswer,
|
||||
SingleChoiceAssessmentData,
|
||||
SingleChoiceQuestion,
|
||||
SingleChoiceQuestionAnswer,
|
||||
SingleChoiceQuestionData
|
||||
} from '@hcengineering/questions'
|
||||
import { IndexKind, type Ref, type Type } from '@hcengineering/core'
|
||||
import { Hidden, Index, Model, Prop, TypeRef, UX } from '@hcengineering/model'
|
||||
import core, { TType } from '@hcengineering/model-core'
|
||||
import questions from '../../plugin'
|
||||
import { TAnswer, TAssessment, TQuestion } from '../base'
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.SingleChoiceQuestionData, core.class.Type)
|
||||
@UX(questions.string.Question)
|
||||
export class TSingleChoiceQuestionData extends TType {}
|
||||
|
||||
/** @public */
|
||||
export function TypeSingleChoiceQuestionData (): Type<SingleChoiceQuestionData> {
|
||||
return {
|
||||
_class: questions.class.SingleChoiceQuestionData,
|
||||
label: questions.string.SingleChoiceQuestion
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.SingleChoiceAssessmentData, core.class.Type)
|
||||
@UX(questions.string.Assessment)
|
||||
export class TSingleChoiceAssessmentData extends TType {}
|
||||
|
||||
/** @public */
|
||||
export function TypeSingleChoiceAssessmentData (): Type<SingleChoiceAssessmentData> {
|
||||
return {
|
||||
_class: questions.class.SingleChoiceAssessmentData,
|
||||
label: questions.string.SingleChoiceAssessment
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.SingleChoiceAnswerData, core.class.Type)
|
||||
@UX(questions.string.Answer)
|
||||
export class TSingleChoiceAnswerData extends TType {}
|
||||
|
||||
/** @public */
|
||||
export function TypeSingleChoiceAnswerData (): Type<SingleChoiceAnswerData> {
|
||||
return {
|
||||
_class: questions.class.SingleChoiceAnswerData,
|
||||
label: questions.string.SingleChoice
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.SingleChoiceQuestion, questions.class.Question)
|
||||
@UX(questions.string.SingleChoiceQuestion)
|
||||
export class TSingleChoiceQuestion extends TQuestion<SingleChoiceQuestionData> implements SingleChoiceQuestion {
|
||||
@Prop(TypeSingleChoiceQuestionData(), questions.string.Question)
|
||||
declare questionData: SingleChoiceQuestionData
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.SingleChoiceQuestionAnswer, questions.class.Answer)
|
||||
@UX(questions.string.SingleChoice)
|
||||
export class TSingleChoiceQuestionAnswer
|
||||
extends TAnswer<SingleChoiceQuestion, SingleChoiceAnswerData>
|
||||
implements SingleChoiceQuestionAnswer {
|
||||
@Prop(TypeRef(questions.class.SingleChoiceQuestion), questions.string.Question)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare question: Ref<SingleChoiceQuestion>
|
||||
|
||||
@Prop(TypeSingleChoiceAnswerData(), questions.string.Answer)
|
||||
declare answerData: SingleChoiceAnswerData
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.SingleChoiceAssessment, questions.class.Assessment)
|
||||
@UX(questions.string.SingleChoiceAssessment)
|
||||
export class TSingleChoiceAssessment
|
||||
extends TAssessment<SingleChoiceQuestionData, SingleChoiceAssessmentData>
|
||||
implements SingleChoiceAssessment {
|
||||
@Prop(TypeSingleChoiceAssessmentData(), questions.string.Assessment)
|
||||
declare assessmentData: SingleChoiceAssessmentData
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@Model(questions.class.SingleChoiceAssessmentAnswer, questions.class.Answer)
|
||||
@UX(questions.string.SingleChoice)
|
||||
export class TSingleChoiceAssessmentAnswer
|
||||
extends TAnswer<SingleChoiceAssessment, SingleChoiceAnswerData>
|
||||
implements SingleChoiceAssessmentAnswer {
|
||||
@Prop(TypeRef(questions.class.SingleChoiceAssessment), questions.string.Question)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare question: Ref<SingleChoiceAssessment>
|
||||
|
||||
@Prop(TypeSingleChoiceAnswerData(), questions.string.Answer)
|
||||
declare answerData: SingleChoiceAnswerData
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
//
|
||||
// Copyright @ 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
|
||||
import type {
|
||||
Answer,
|
||||
MultipleChoiceAssessment,
|
||||
MultipleChoiceAssessmentAnswer,
|
||||
MultipleChoiceQuestion,
|
||||
MultipleChoiceQuestionAnswer,
|
||||
OrderingAssessment,
|
||||
OrderingAssessmentAnswer,
|
||||
OrderingQuestion,
|
||||
OrderingQuestionAnswer,
|
||||
Question,
|
||||
QuestionMixin,
|
||||
SingleChoiceAssessment,
|
||||
SingleChoiceAssessmentAnswer,
|
||||
SingleChoiceQuestion,
|
||||
SingleChoiceQuestionAnswer
|
||||
} from '@hcengineering/questions'
|
||||
import { type Class, type MixinData, type Ref } from '@hcengineering/core'
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/model-core'
|
||||
import tracker from '@hcengineering/model-tracker'
|
||||
import view, { createAction } from '@hcengineering/model-view'
|
||||
import {
|
||||
TAnswer,
|
||||
TAssessment,
|
||||
TMultipleChoiceAnswerData,
|
||||
TMultipleChoiceAssessment,
|
||||
TMultipleChoiceAssessmentAnswer,
|
||||
TMultipleChoiceAssessmentData,
|
||||
TMultipleChoiceQuestion,
|
||||
TMultipleChoiceQuestionAnswer,
|
||||
TMultipleChoiceQuestionData,
|
||||
TOrderingAnswerData,
|
||||
TOrderingAssessment,
|
||||
TOrderingAssessmentAnswer,
|
||||
TOrderingAssessmentData,
|
||||
TOrderingQuestion,
|
||||
TOrderingQuestionAnswer,
|
||||
TOrderingQuestionData,
|
||||
TQuestion,
|
||||
TQuestionMixin,
|
||||
TSingleChoiceAnswerData,
|
||||
TSingleChoiceAssessment,
|
||||
TSingleChoiceAssessmentAnswer,
|
||||
TSingleChoiceAssessmentData,
|
||||
TSingleChoiceQuestion,
|
||||
TSingleChoiceQuestionAnswer,
|
||||
TSingleChoiceQuestionData,
|
||||
TTypePercentage,
|
||||
TTypeQuestionOption,
|
||||
TTypeRank
|
||||
} from './doc-types'
|
||||
import questions from './plugin'
|
||||
|
||||
export { questionsOperation } from './migration'
|
||||
export { default } from './plugin'
|
||||
export { questionsId } from '@hcengineering/questions/src/index'
|
||||
export * from './doc-types'
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TTypeRank)
|
||||
|
||||
builder.createModel(TTypePercentage)
|
||||
builder.mixin(questions.type.Percentage, core.class.Class, view.mixin.AttributeEditor, {
|
||||
inlineEditor: view.component.NumberEditor
|
||||
})
|
||||
|
||||
builder.createModel(TTypeQuestionOption)
|
||||
builder.createModel(TQuestion, TAssessment, TAnswer)
|
||||
builder.createModel(TQuestionMixin)
|
||||
|
||||
defineActions(builder)
|
||||
defineSingleChoice(builder)
|
||||
defineMultipleChoice(builder)
|
||||
defineOrdering(builder)
|
||||
}
|
||||
|
||||
function defineSingleChoice (builder: Builder): void {
|
||||
builder.createModel(TSingleChoiceQuestionData, TSingleChoiceAnswerData)
|
||||
|
||||
defineQuestion<SingleChoiceQuestion, SingleChoiceQuestionAnswer>(
|
||||
builder,
|
||||
questions.class.SingleChoiceQuestion,
|
||||
TSingleChoiceQuestion,
|
||||
questions.class.SingleChoiceQuestionAnswer,
|
||||
TSingleChoiceQuestionAnswer,
|
||||
{
|
||||
answerClassRef: questions.class.SingleChoiceQuestionAnswer,
|
||||
answerDataAssess: null,
|
||||
answerDataEditor: questions.component.SingleChoiceQuestionAnswerDataEditor,
|
||||
answerDataPresenter: questions.component.SingleChoiceQuestionAnswerDataPresenter,
|
||||
questionInit: questions.function.SingleChoiceQuestionInit,
|
||||
questionDataEditor: questions.component.SingleChoiceQuestionDataEditor,
|
||||
questionDataPresenter: questions.component.SingleChoiceQuestionDataPresenter
|
||||
}
|
||||
)
|
||||
|
||||
builder.createModel(TSingleChoiceAssessmentData)
|
||||
|
||||
defineQuestion<SingleChoiceAssessment, SingleChoiceAssessmentAnswer>(
|
||||
builder,
|
||||
questions.class.SingleChoiceAssessment,
|
||||
TSingleChoiceAssessment,
|
||||
questions.class.SingleChoiceAssessmentAnswer,
|
||||
TSingleChoiceAssessmentAnswer,
|
||||
{
|
||||
answerClassRef: questions.class.SingleChoiceAssessmentAnswer,
|
||||
answerDataAssess: questions.function.SingleChoiceAssessmentAssess,
|
||||
answerDataEditor: questions.component.SingleChoiceAssessmentAnswerDataEditor,
|
||||
answerDataPresenter: questions.component.SingleChoiceAssessmentAnswerDataPresenter,
|
||||
questionInit: questions.function.SingleChoiceAssessmentInit,
|
||||
questionDataEditor: questions.component.SingleChoiceAssessmentDataEditor,
|
||||
questionDataPresenter: questions.component.SingleChoiceAssessmentDataPresenter
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function defineMultipleChoice (builder: Builder): void {
|
||||
builder.createModel(TMultipleChoiceQuestionData, TMultipleChoiceAnswerData)
|
||||
|
||||
defineQuestion<MultipleChoiceQuestion, MultipleChoiceQuestionAnswer>(
|
||||
builder,
|
||||
questions.class.MultipleChoiceQuestion,
|
||||
TMultipleChoiceQuestion,
|
||||
questions.class.MultipleChoiceQuestionAnswer,
|
||||
TMultipleChoiceQuestionAnswer,
|
||||
{
|
||||
answerClassRef: questions.class.MultipleChoiceQuestionAnswer,
|
||||
answerDataAssess: null,
|
||||
answerDataEditor: questions.component.MultipleChoiceQuestionAnswerDataEditor,
|
||||
answerDataPresenter: questions.component.MultipleChoiceQuestionAnswerDataPresenter,
|
||||
questionInit: questions.function.MultipleChoiceQuestionInit,
|
||||
questionDataEditor: questions.component.MultipleChoiceQuestionDataEditor,
|
||||
questionDataPresenter: questions.component.MultipleChoiceQuestionDataPresenter
|
||||
}
|
||||
)
|
||||
|
||||
builder.createModel(TMultipleChoiceAssessmentData)
|
||||
|
||||
defineQuestion<MultipleChoiceAssessment, MultipleChoiceAssessmentAnswer>(
|
||||
builder,
|
||||
questions.class.MultipleChoiceAssessment,
|
||||
TMultipleChoiceAssessment,
|
||||
questions.class.MultipleChoiceAssessmentAnswer,
|
||||
TMultipleChoiceAssessmentAnswer,
|
||||
{
|
||||
answerClassRef: questions.class.MultipleChoiceAssessmentAnswer,
|
||||
answerDataAssess: questions.function.MultipleChoiceAssessmentAssess,
|
||||
answerDataEditor: questions.component.MultipleChoiceAssessmentAnswerDataEditor,
|
||||
answerDataPresenter: questions.component.MultipleChoiceAssessmentAnswerDataPresenter,
|
||||
questionInit: questions.function.MultipleChoiceAssessmentInit,
|
||||
questionDataEditor: questions.component.MultipleChoiceAssessmentDataEditor,
|
||||
questionDataPresenter: questions.component.MultipleChoiceAssessmentDataPresenter
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function defineOrdering (builder: Builder): void {
|
||||
builder.createModel(TOrderingQuestionData, TOrderingAnswerData)
|
||||
|
||||
defineQuestion<OrderingQuestion, OrderingQuestionAnswer>(
|
||||
builder,
|
||||
questions.class.OrderingQuestion,
|
||||
TOrderingQuestion,
|
||||
questions.class.OrderingQuestionAnswer,
|
||||
TOrderingQuestionAnswer,
|
||||
{
|
||||
answerClassRef: questions.class.OrderingQuestionAnswer,
|
||||
answerDataAssess: null,
|
||||
answerDataEditor: questions.component.OrderingQuestionAnswerDataEditor,
|
||||
answerDataPresenter: questions.component.OrderingQuestionAnswerDataPresenter,
|
||||
questionInit: questions.function.OrderingQuestionInit,
|
||||
questionDataEditor: questions.component.OrderingQuestionDataEditor,
|
||||
questionDataPresenter: questions.component.OrderingQuestionDataPresenter
|
||||
}
|
||||
)
|
||||
|
||||
builder.createModel(TOrderingAssessmentData)
|
||||
|
||||
defineQuestion<OrderingAssessment, OrderingAssessmentAnswer>(
|
||||
builder,
|
||||
questions.class.OrderingAssessment,
|
||||
TOrderingAssessment,
|
||||
questions.class.OrderingAssessmentAnswer,
|
||||
TOrderingAssessmentAnswer,
|
||||
{
|
||||
answerClassRef: questions.class.OrderingAssessmentAnswer,
|
||||
answerDataAssess: questions.function.OrderingAssessmentAssess,
|
||||
answerDataEditor: questions.component.OrderingAssessmentAnswerDataEditor,
|
||||
answerDataPresenter: questions.component.OrderingAssessmentAnswerDataPresenter,
|
||||
questionInit: questions.function.OrderingAssessmentInit,
|
||||
questionDataEditor: questions.component.OrderingAssessmentDataEditor,
|
||||
questionDataPresenter: questions.component.OrderingAssessmentDataPresenter
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function defineActions (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
view.class.ActionCategory,
|
||||
core.space.Model,
|
||||
{
|
||||
label: questions.string.Questions,
|
||||
visible: true
|
||||
},
|
||||
questions.actionCategory.Questions
|
||||
)
|
||||
|
||||
builder.mixin(questions.class.Question, core.class.Class, view.mixin.IgnoreActions, {
|
||||
actions: [view.action.Open, view.action.OpenInNewTab, view.action.Delete, tracker.action.NewRelatedIssue]
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: questions.action.QuestionMoveUpAction,
|
||||
visibilityTester: questions.action.QuestionMoveUpIsAvailable,
|
||||
input: 'focus',
|
||||
label: view.string.MoveUp,
|
||||
icon: questions.icon.ArrowUp,
|
||||
target: questions.class.Question,
|
||||
context: { mode: ['context', 'workbench'], group: 'edit' },
|
||||
category: questions.actionCategory.Questions
|
||||
},
|
||||
questions.action.QuestionMoveUp
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: questions.action.QuestionMoveDownAction,
|
||||
visibilityTester: questions.action.QuestionMoveDownIsAvailable,
|
||||
input: 'focus',
|
||||
label: view.string.MoveDown,
|
||||
icon: questions.icon.ArrowDown,
|
||||
target: questions.class.Question,
|
||||
context: { mode: ['context', 'workbench'], group: 'edit' },
|
||||
category: questions.actionCategory.Questions
|
||||
},
|
||||
questions.action.QuestionMoveDown
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: questions.action.QuestionDuplicateAction,
|
||||
visibilityTester: questions.action.QuestionDuplicateIsAvailable,
|
||||
input: 'focus',
|
||||
label: questions.string.Duplicate,
|
||||
icon: questions.icon.Duplicate,
|
||||
target: questions.class.Question,
|
||||
context: { mode: ['context', 'workbench'], group: 'copy' },
|
||||
category: questions.actionCategory.Questions
|
||||
},
|
||||
questions.action.QuestionDuplicate
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
override: [view.action.Delete],
|
||||
action: questions.action.QuestionDeleteAction,
|
||||
visibilityTester: questions.action.QuestionDeleteIsAvailable,
|
||||
input: 'any',
|
||||
label: view.string.Delete,
|
||||
icon: view.icon.Delete,
|
||||
target: questions.class.Question,
|
||||
context: { mode: ['context', 'workbench'], group: 'remove' },
|
||||
category: questions.actionCategory.Questions
|
||||
},
|
||||
questions.action.QuestionDelete
|
||||
)
|
||||
|
||||
builder.mixin(questions.class.Answer, core.class.Class, view.mixin.IgnoreActions, {
|
||||
actions: [view.action.Open, view.action.OpenInNewTab, view.action.Delete, tracker.action.NewRelatedIssue]
|
||||
})
|
||||
}
|
||||
|
||||
function defineQuestion<Q extends Question<any>, A extends Answer<Q, any>> (
|
||||
builder: Builder,
|
||||
questionClassRef: Ref<Class<Q>>,
|
||||
questionModelClass: new () => Q,
|
||||
answerClassRef: Ref<Class<A>>,
|
||||
answerModelClass: new () => A,
|
||||
attributes: MixinData<Class<Q>, QuestionMixin<Q, A>>
|
||||
): void {
|
||||
builder.createModel(questionModelClass, answerModelClass)
|
||||
builder.mixin<Class<Q>, QuestionMixin<Q, A>>(questionClassRef, core.class.Class, questions.mixin.QuestionMixin, {
|
||||
...attributes,
|
||||
answerClassRef
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
//
|
||||
// Copyright @ 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
|
||||
import { type MigrateOperation, type MigrationClient, type MigrationUpgradeClient } from '@hcengineering/model'
|
||||
|
||||
export const questionsOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {},
|
||||
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// Copyright @ 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
|
||||
import { questionsId } from '@hcengineering/questions'
|
||||
import questions from '@hcengineering/questions-resources/src/plugin'
|
||||
import type { Ref } from '@hcengineering/core'
|
||||
import { mergeIds } from '@hcengineering/platform'
|
||||
import type { ActionCategory } from '@hcengineering/view'
|
||||
|
||||
export default mergeIds(questionsId, questions, {
|
||||
actionCategory: {
|
||||
Questions: '' as Ref<ActionCategory>
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig",
|
||||
"rigProfile": "model"
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "@hcengineering/model-server-controlled-documents",
|
||||
"version": "0.1.0",
|
||||
"main": "lib/index.js",
|
||||
"svelte": "src/index.ts",
|
||||
"types": "types/index.d.ts",
|
||||
"author": "Hardcore Engineering Inc.",
|
||||
"template": "@hcengineering/model-package",
|
||||
"scripts": {
|
||||
"build": "compile",
|
||||
"build:watch": "compile",
|
||||
"format": "format src",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"prettier": "^3.1.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/core": "^0.6.28",
|
||||
"@hcengineering/model": "^0.6.7",
|
||||
"@hcengineering/platform": "^0.6.9",
|
||||
"@hcengineering/server-core": "^0.6.1",
|
||||
"@hcengineering/model-core": "^0.6.0",
|
||||
"@hcengineering/request": "^0.6.6",
|
||||
"@hcengineering/contact": "^0.6.20",
|
||||
"@hcengineering/controlled-documents": "^0.1.0",
|
||||
"@hcengineering/server-controlled-documents": "^0.1.0",
|
||||
"@hcengineering/server-notification": "^0.6.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// Copyright © 2023 Hardcore Engineering Inc.
|
||||
//
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/core'
|
||||
import serverCore from '@hcengineering/server-core'
|
||||
import { RequestStatus } from '@hcengineering/request'
|
||||
import documents, { DocumentState } from '@hcengineering/controlled-documents'
|
||||
import serverDocuments from '@hcengineering/server-controlled-documents'
|
||||
import contact from '@hcengineering/contact'
|
||||
import serverNotification from '@hcengineering/server-notification'
|
||||
|
||||
export { serverDocumentsId } from '@hcengineering/server-controlled-documents/src/index'
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverDocuments.trigger.OnCollaborativeSectionDeleted,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
'tx.objectClass': documents.class.CollaborativeDocumentSection,
|
||||
'tx._class': core.class.TxRemoveDoc
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverDocuments.trigger.OnDocPlannedEffectiveDateChanged,
|
||||
txMatch: {
|
||||
_class: core.class.TxUpdateDoc,
|
||||
objectClass: documents.class.ControlledDocument
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverDocuments.trigger.OnDocApprovalRequestApproved,
|
||||
txMatch: {
|
||||
_class: core.class.TxCollectionCUD,
|
||||
objectClass: documents.class.ControlledDocument,
|
||||
'tx._class': core.class.TxUpdateDoc,
|
||||
'tx.objectClass': documents.class.DocumentApprovalRequest,
|
||||
'tx.operations.status': RequestStatus.Completed
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverDocuments.trigger.OnDocHasBecomeEffective,
|
||||
txMatch: {
|
||||
_class: core.class.TxUpdateDoc,
|
||||
objectClass: documents.class.ControlledDocument,
|
||||
'operations.state': DocumentState.Effective
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverDocuments.trigger.OnWorkspaceOwnerAdded,
|
||||
txMatch: {
|
||||
objectClass: contact.class.PersonAccount
|
||||
}
|
||||
})
|
||||
|
||||
builder.mixin(documents.class.DocumentMeta, core.class.Class, serverCore.mixin.SearchPresenter, {
|
||||
searchConfig: {
|
||||
iconConfig: {
|
||||
component: documents.component.DocumentIcon,
|
||||
props: []
|
||||
},
|
||||
title: 'title'
|
||||
}
|
||||
})
|
||||
|
||||
builder.mixin(documents.class.ControlledDocument, core.class.Class, serverNotification.mixin.TextPresenter, {
|
||||
presenter: serverDocuments.function.ControlledDocumentTextPresenter
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig",
|
||||
"rigProfile": "model"
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@hcengineering/model-server-products",
|
||||
"version": "0.1.0",
|
||||
"main": "lib/index.js",
|
||||
"svelte": "src/index.ts",
|
||||
"types": "types/index.d.ts",
|
||||
"author": "Hardcore Engineering Inc.",
|
||||
"template": "@hcengineering/model-package",
|
||||
"scripts": {
|
||||
"build": "compile",
|
||||
"build:watch": "compile",
|
||||
"format": "format src",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"prettier": "^3.1.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/core": "^0.6.28",
|
||||
"@hcengineering/model": "^0.6.7",
|
||||
"@hcengineering/platform": "^0.6.9",
|
||||
"@hcengineering/server-core": "^0.6.1",
|
||||
"@hcengineering/model-core": "^0.6.0",
|
||||
"@hcengineering/products": "~0.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
//
|
||||
|
||||
import type { Plugin } from '@hcengineering/platform'
|
||||
import products from '@hcengineering/products'
|
||||
|
||||
import core from '@hcengineering/core'
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
import serverCore from '@hcengineering/server-core'
|
||||
|
||||
export const serverProductsId = 'server-products' as Plugin
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.mixin(products.class.Product, core.class.Class, serverCore.mixin.SearchPresenter, {
|
||||
searchConfig: {
|
||||
iconConfig: {
|
||||
component: products.component.ProductSearchIcon,
|
||||
props: ['icon', 'color']
|
||||
},
|
||||
title: 'name'
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig",
|
||||
"rigProfile": "model"
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@hcengineering/model-server-training",
|
||||
"version": "0.1.0",
|
||||
"main": "lib/index.js",
|
||||
"svelte": "src/index.ts",
|
||||
"types": "types/index.d.ts",
|
||||
"author": "Hardcore Engineering Inc",
|
||||
"template": "@hcengineering/model-package",
|
||||
"scripts": {
|
||||
"build": "compile",
|
||||
"build:watch": "compile",
|
||||
"format": "format src",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"prettier": "^3.1.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/core": "^0.6.28",
|
||||
"@hcengineering/model": "^0.6.7",
|
||||
"@hcengineering/notification": "^0.6.16",
|
||||
"@hcengineering/platform": "^0.6.9",
|
||||
"@hcengineering/server-core": "^0.6.1",
|
||||
"@hcengineering/server-notification": "^0.6.1",
|
||||
"@hcengineering/model-training": "^0.1.0",
|
||||
"@hcengineering/server-training": "^0.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// Copyright @ 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
import { type Builder } from '@hcengineering/model'
|
||||
|
||||
import training from '@hcengineering/model-training'
|
||||
import serverTraining from '@hcengineering/server-training'
|
||||
import core from '@hcengineering/core'
|
||||
import notification from '@hcengineering/notification'
|
||||
import serverNotification from '@hcengineering/server-notification'
|
||||
|
||||
export { serverTrainingId } from '@hcengineering/server-training/src/index'
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.mixin(
|
||||
training.notification.TrainingRequest,
|
||||
notification.class.NotificationType,
|
||||
serverNotification.mixin.TypeMatch,
|
||||
{
|
||||
func: serverTraining.function.TrainingRequestNotificationTypeMatch
|
||||
}
|
||||
)
|
||||
|
||||
builder.mixin(training.class.TrainingRequest, core.class.Class, serverNotification.mixin.TextPresenter, {
|
||||
presenter: serverTraining.function.TrainingRequestTextPresenter
|
||||
})
|
||||
|
||||
builder.mixin(training.class.TrainingRequest, core.class.Class, serverNotification.mixin.HTMLPresenter, {
|
||||
presenter: serverTraining.function.TrainingRequestHTMLPresenter
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
|
||||
roots: ["./src"],
|
||||
coverageReporters: ["text-summary", "html"]
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"name": "@hcengineering/model-training",
|
||||
"version": "0.1.0",
|
||||
"main": "lib/index.js",
|
||||
"svelte": "src/index.ts",
|
||||
"types": "types/index.d.ts",
|
||||
"author": "Hardcore Engineering Inc",
|
||||
"scripts": {
|
||||
"build": "compile",
|
||||
"build:watch": "compile",
|
||||
"format": "format src",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:test": "jest --passWithNoTests --silent",
|
||||
"_phase:format": "format src",
|
||||
"test": "jest --passWithNoTests --silent",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint": "^8.54.0",
|
||||
"prettier": "^3.1.0",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"@types/jest": "^29.5.5",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@hcengineering/model-questions": "^0.1.0",
|
||||
"@hcengineering/questions": "^0.1.0",
|
||||
"@hcengineering/training": "^0.1.0",
|
||||
"@hcengineering/training-resources": "^0.1.0",
|
||||
"@hcengineering/activity": "^0.6.0",
|
||||
"@hcengineering/attachment": "^0.6.9",
|
||||
"@hcengineering/contact": "^0.6.20",
|
||||
"@hcengineering/core": "^0.6.28",
|
||||
"@hcengineering/model": "^0.6.7",
|
||||
"@hcengineering/model-attachment": "^0.6.0",
|
||||
"@hcengineering/model-contact": "^0.6.1",
|
||||
"@hcengineering/model-core": "^0.6.0",
|
||||
"@hcengineering/model-notification": "^0.6.0",
|
||||
"@hcengineering/model-tracker": "^0.6.0",
|
||||
"@hcengineering/model-view": "^0.6.0",
|
||||
"@hcengineering/model-workbench": "^0.6.1",
|
||||
"@hcengineering/notification": "^0.6.16",
|
||||
"@hcengineering/platform": "^0.6.9",
|
||||
"@hcengineering/setting": "^0.6.11",
|
||||
"@hcengineering/ui": "^0.6.11",
|
||||
"@hcengineering/view": "^0.6.9",
|
||||
"@hcengineering/workbench": "^0.6.9",
|
||||
"@hcengineering/model-print": "^0.6.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,897 @@
|
||||
//
|
||||
// Copyright © 2024 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.
|
||||
//
|
||||
|
||||
import activity from '@hcengineering/activity'
|
||||
import notification, { type NotificationType } from '@hcengineering/notification'
|
||||
import { type Asset, type IntlString } from '@hcengineering/platform'
|
||||
import type { BuildModelKey, Viewlet, ViewletDescriptor } from '@hcengineering/view'
|
||||
import questions from '@hcengineering/model-questions'
|
||||
import contact from '@hcengineering/contact'
|
||||
import tracker from '@hcengineering/model-tracker'
|
||||
import attachment from '@hcengineering/model-attachment'
|
||||
import print from '@hcengineering/model-print'
|
||||
import setting, { getRoleAttributeProps } from '@hcengineering/setting'
|
||||
import {
|
||||
type Training,
|
||||
type TrainingAttempt,
|
||||
trainingId,
|
||||
type TrainingRequest,
|
||||
TrainingSpecialIds
|
||||
} from '@hcengineering/training'
|
||||
|
||||
import { AccountRole, type Data, type FindOptions, type Permission, type Ref } from '@hcengineering/core'
|
||||
import { Prop, type Builder } from '@hcengineering/model'
|
||||
|
||||
import contacts from '@hcengineering/model-contact'
|
||||
import core from '@hcengineering/model-core'
|
||||
import view, { classPresenter, createAction } from '@hcengineering/model-view'
|
||||
import workbench from '@hcengineering/model-workbench'
|
||||
import training from './plugin'
|
||||
import {
|
||||
TSequence,
|
||||
TTraining,
|
||||
TTrainingAttempt,
|
||||
TTrainingAttemptState,
|
||||
TTrainingRequest,
|
||||
TTrainingState,
|
||||
TTrainingsTypeData
|
||||
} from './types'
|
||||
import { roles } from './roles'
|
||||
export { trainingOperation } from './migration'
|
||||
export { trainingId } from '@hcengineering/training/src/index'
|
||||
export { default } from './plugin'
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
defineBase(builder)
|
||||
defineSpaceType(builder)
|
||||
defineTraining(builder)
|
||||
defineTrainingRequest(builder)
|
||||
defineTrainingAttempt(builder)
|
||||
defineApplication(builder)
|
||||
defineSettings(builder)
|
||||
}
|
||||
|
||||
function defineBase (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
view.class.ActionCategory,
|
||||
core.space.Model,
|
||||
{
|
||||
label: training.string.TrainingApplication,
|
||||
visible: true
|
||||
},
|
||||
training.actionCategory.Training
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
notification.class.NotificationGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
label: training.string.TrainingApplication,
|
||||
icon: training.icon.TrainingApplication
|
||||
},
|
||||
training.notification.TrainingGroup
|
||||
)
|
||||
|
||||
builder.createDoc(notification.class.ActivityNotificationViewlet, core.space.Model, {
|
||||
messageMatch: {
|
||||
_class: activity.class.DocUpdateMessage,
|
||||
objectClass: training.class.TrainingRequest
|
||||
},
|
||||
presenter: training.component.TrainingRequestNotificationPresenter
|
||||
})
|
||||
|
||||
builder.createModel(TSequence)
|
||||
}
|
||||
|
||||
function defineSpaceType (builder: Builder): void {
|
||||
for (const role of roles) {
|
||||
const { label, roleType } = getRoleAttributeProps(role.name)
|
||||
|
||||
Prop(roleType, label)(TTrainingsTypeData.prototype, role._id)
|
||||
}
|
||||
|
||||
builder.createModel(TTrainingsTypeData)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.SpaceTypeDescriptor,
|
||||
core.space.Model,
|
||||
{
|
||||
name: training.string.TrainingApplication,
|
||||
description: training.string.TrainingApplication,
|
||||
icon: training.icon.TrainingApplication,
|
||||
baseClass: core.class.TypedSpace,
|
||||
availablePermissions: [
|
||||
training.permission.CreateTraining,
|
||||
training.permission.ChangeSomeoneElsesTrainingOwner,
|
||||
training.permission.ViewSomeoneElsesTrainingOverview,
|
||||
training.permission.ViewSomeoneElsesTrainingQuestions,
|
||||
training.permission.CreateRequestOnSomeoneElsesTraining,
|
||||
training.permission.ChangeSomeoneElsesSentRequestOwner,
|
||||
training.permission.ViewSomeoneElsesSentRequest,
|
||||
training.permission.ViewSomeoneElsesTraineesResults
|
||||
]
|
||||
},
|
||||
training.spaceTypeDescriptor.Trainings
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.SpaceType,
|
||||
core.space.Model,
|
||||
{
|
||||
name: 'Default Trainings',
|
||||
descriptor: training.spaceTypeDescriptor.Trainings,
|
||||
roles: roles.length,
|
||||
targetClass: training.mixin.TrainingsTypeData
|
||||
},
|
||||
training.spaceType.Trainings
|
||||
)
|
||||
|
||||
for (const role of roles) {
|
||||
builder.createDoc(
|
||||
core.class.Role,
|
||||
core.space.Model,
|
||||
{
|
||||
attachedTo: training.spaceType.Trainings,
|
||||
attachedToClass: core.class.SpaceType,
|
||||
collection: 'roles',
|
||||
name: role.name,
|
||||
permissions: role.permissions
|
||||
},
|
||||
role._id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function defineTraining (builder: Builder): void {
|
||||
builder.createModel(TTrainingState)
|
||||
|
||||
builder.mixin(training.class.TypeTrainingState, core.class.Class, view.mixin.SortFuncs, {
|
||||
func: training.function.TrainingStateSort
|
||||
})
|
||||
|
||||
builder.mixin(training.class.TypeTrainingState, core.class.Class, view.mixin.AllValuesFunc, {
|
||||
func: training.function.TrainingStateAllValues
|
||||
})
|
||||
|
||||
builder.mixin(training.class.TypeTrainingState, core.class.Class, view.mixin.AttributeFilterPresenter, {
|
||||
presenter: training.component.TrainingStateFilterPresenter
|
||||
})
|
||||
|
||||
builder.mixin(training.class.TypeTrainingState, core.class.Class, view.mixin.AttributeFilter, {
|
||||
component: view.component.ValueFilter
|
||||
})
|
||||
|
||||
classPresenter(builder, training.class.TypeTrainingState, training.component.TrainingStatePresenter)
|
||||
|
||||
builder.createModel(TTraining)
|
||||
|
||||
builder.mixin(training.class.Training, core.class.Class, view.mixin.LinkProvider, {
|
||||
encode: training.function.TrainingLinkProviderEncode
|
||||
})
|
||||
|
||||
builder.mixin(training.class.Training, core.class.Class, view.mixin.ObjectPanel, {
|
||||
component: training.component.TrainingPanel
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: training.class.Training,
|
||||
descriptor: view.viewlet.Table,
|
||||
config: [
|
||||
{
|
||||
key: '',
|
||||
sortingKey: 'seqNumber',
|
||||
label: training.string.Training,
|
||||
presenter: training.component.TrainingCodePresenter
|
||||
},
|
||||
{
|
||||
...columns.trainingTitle,
|
||||
key: 'title'
|
||||
},
|
||||
{
|
||||
...columns.trainingRevision,
|
||||
key: 'revision',
|
||||
sortingKey: 'revision'
|
||||
},
|
||||
{
|
||||
key: 'state',
|
||||
sortingKey: 'state',
|
||||
displayProps: { align: 'center' }
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
sortingKey: 'passingScore',
|
||||
label: training.string.TrainingPassingScore,
|
||||
presenter: training.component.TrainingPassingScorePresenter,
|
||||
displayProps: { align: 'center' }
|
||||
},
|
||||
{
|
||||
key: 'attachments',
|
||||
label: attachment.string.Attachments,
|
||||
props: {
|
||||
canAdd: false,
|
||||
canRemove: false
|
||||
},
|
||||
displayProps: { align: 'center' }
|
||||
},
|
||||
{
|
||||
key: 'requests',
|
||||
label: training.string.TrainingRequests,
|
||||
presenter: view.component.NumberPresenter,
|
||||
displayProps: { align: 'center' }
|
||||
},
|
||||
'modifiedOn',
|
||||
{
|
||||
...columns.owner,
|
||||
key: 'owner'
|
||||
}
|
||||
],
|
||||
configOptions: {
|
||||
strict: true,
|
||||
sortable: true
|
||||
}
|
||||
})
|
||||
|
||||
builder.mixin(training.class.Training, core.class.Class, view.mixin.ClassFilters, {
|
||||
filters: ['revision', 'state', 'modifiedOn'] as Array<keyof Training>,
|
||||
strict: true
|
||||
})
|
||||
|
||||
builder.mixin(training.class.Training, core.class.Class, view.mixin.IgnoreActions, {
|
||||
actions: [view.action.Delete, tracker.action.NewRelatedIssue, print.action.Print]
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: training.action.TrainingChangeOwnerAction,
|
||||
visibilityTester: training.action.TrainingChangeOwnerIsAvailable,
|
||||
input: 'focus',
|
||||
label: training.string.ChangeOwner,
|
||||
icon: contact.icon.Person,
|
||||
target: training.class.Training,
|
||||
context: { mode: ['context'], group: 'edit' },
|
||||
category: training.actionCategory.Training
|
||||
},
|
||||
training.action.TrainingChangeOwner
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: training.action.TrainingReleaseAction,
|
||||
visibilityTester: training.action.TrainingReleaseIsAvailable,
|
||||
input: 'focus',
|
||||
label: training.string.TrainingRelease,
|
||||
icon: training.icon.Release,
|
||||
target: training.class.Training,
|
||||
context: { mode: ['context'], group: 'edit' },
|
||||
category: training.actionCategory.Training
|
||||
},
|
||||
training.action.TrainingRelease
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: training.action.TrainingDuplicateAction,
|
||||
visibilityTester: training.action.TrainingDuplicateIsAvailable,
|
||||
input: 'focus',
|
||||
label: training.string.Duplicate,
|
||||
icon: training.icon.Duplicate,
|
||||
target: training.class.Training,
|
||||
context: { mode: ['context'], group: 'copy' },
|
||||
category: training.actionCategory.Training
|
||||
},
|
||||
training.action.TrainingDuplicate
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: training.action.TrainingDraftAction,
|
||||
visibilityTester: training.action.TrainingDraftIsAvailable,
|
||||
input: 'focus',
|
||||
label: training.string.CreateNewVersion,
|
||||
icon: training.icon.Duplicate,
|
||||
target: training.class.Training,
|
||||
context: { mode: ['context'], group: 'copy' },
|
||||
category: training.actionCategory.Training
|
||||
},
|
||||
training.action.TrainingDraft
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: training.action.TrainingRequestCreateAction,
|
||||
visibilityTester: training.action.TrainingRequestCreateIsAvailable,
|
||||
input: 'focus',
|
||||
label: training.string.TrainingRequestAssign,
|
||||
icon: training.icon.ViewSentRequests,
|
||||
target: training.class.Training,
|
||||
context: { mode: ['context'], group: 'tools' },
|
||||
category: training.actionCategory.Training
|
||||
},
|
||||
training.action.TrainingRequestCreate
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
override: [view.action.Delete],
|
||||
action: training.action.TrainingDeleteAction,
|
||||
visibilityTester: training.action.TrainingDeleteIsAvailable,
|
||||
input: 'focus',
|
||||
label: view.string.Delete,
|
||||
icon: view.icon.Delete,
|
||||
target: training.class.Training,
|
||||
context: { mode: ['context'], group: 'remove' },
|
||||
category: training.actionCategory.Training
|
||||
},
|
||||
training.action.TrainingDelete
|
||||
)
|
||||
|
||||
definePermission(builder, training.permission.ChangeSomeoneElsesTrainingOwner, {
|
||||
label: training.string.Permission_ChangeSomeoneElsesTrainingOwner,
|
||||
description: training.string.Permission_ChangeSomeoneElsesTrainingOwner_Description
|
||||
})
|
||||
definePermission(builder, training.permission.CreateTraining, {
|
||||
label: training.string.Permission_CreateTraining,
|
||||
description: training.string.Permission_CreateTraining_Description
|
||||
})
|
||||
definePermission(builder, training.permission.ViewSomeoneElsesTrainingOverview, {
|
||||
label: training.string.Permission_ViewSomeoneElsesTrainingOverview,
|
||||
description: training.string.Permission_ViewSomeoneElsesTrainingOverview_Description
|
||||
})
|
||||
definePermission(builder, training.permission.ViewSomeoneElsesTrainingQuestions, {
|
||||
label: training.string.Permission_ViewSomeoneElsesTrainingQuestions,
|
||||
description: training.string.Permission_ViewSomeoneElsesTrainingQuestions_Description
|
||||
})
|
||||
}
|
||||
|
||||
function defineTrainingRequest (builder: Builder): void {
|
||||
builder.createModel(TTrainingRequest)
|
||||
|
||||
builder.mixin(training.class.TrainingRequest, core.class.Class, view.mixin.LinkProvider, {
|
||||
encode: training.function.TrainingRequestLinkProviderEncode
|
||||
})
|
||||
|
||||
builder.mixin(training.class.TrainingRequest, core.class.Class, view.mixin.ObjectPanel, {
|
||||
component: training.component.TrainingRequestPanel
|
||||
})
|
||||
|
||||
const columnSeqNumber: BuildModelKey = {
|
||||
key: '',
|
||||
sortingKey: '$lookup.attachedTo.seqNumber',
|
||||
label: training.string.Training,
|
||||
presenter: training.component.SentRequestPresenter
|
||||
}
|
||||
const columnTrainingTitle: BuildModelKey = {
|
||||
...columns.trainingTitle,
|
||||
key: '$lookup.attachedTo.title'
|
||||
}
|
||||
const columnTrainingRevision: BuildModelKey = {
|
||||
...columns.trainingRevision,
|
||||
key: '$lookup.attachedTo.revision',
|
||||
sortingKey: '$lookup.attachedTo.revision'
|
||||
}
|
||||
const columnSentState: BuildModelKey = {
|
||||
key: '',
|
||||
sortingKey: 'canceledOn',
|
||||
presenter: training.component.SentRequestStatePresenter,
|
||||
label: training.string.State,
|
||||
displayProps: { align: 'center' }
|
||||
}
|
||||
const columnIncomingState: BuildModelKey = {
|
||||
key: '',
|
||||
label: training.string.State,
|
||||
presenter: training.component.IncomingRequestStatePresenter,
|
||||
displayProps: { align: 'center' }
|
||||
}
|
||||
const columnCompletion: BuildModelKey = {
|
||||
key: '',
|
||||
label: training.string.TrainingRequestCompletion,
|
||||
presenter: training.component.SentRequestCompletionPresenter
|
||||
}
|
||||
const columnIncomingAttempts: BuildModelKey = {
|
||||
key: '',
|
||||
label: training.string.TrainingAttempts,
|
||||
presenter: training.component.IncomingRequestAttemptsPresenter,
|
||||
displayProps: { align: 'center' }
|
||||
}
|
||||
const columnMaxAttempts: BuildModelKey = {
|
||||
key: 'maxAttempts',
|
||||
label: training.string.TrainingRequestMaxAttempts,
|
||||
presenter: training.component.TrainingRequestMaxAttemptsPresenter,
|
||||
displayProps: { align: 'center' }
|
||||
}
|
||||
const columnDueDate: BuildModelKey = {
|
||||
key: 'dueDate',
|
||||
label: training.string.TrainingRequestDueDate,
|
||||
presenter: training.component.TrainingRequestDueDateEditor
|
||||
}
|
||||
const columnOwner: BuildModelKey = {
|
||||
...columns.owner,
|
||||
key: 'owner'
|
||||
}
|
||||
const requestsViewletData: Omit<Data<Viewlet>, 'descriptor' | 'config'> = {
|
||||
attachTo: training.class.TrainingRequest,
|
||||
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
||||
options: {
|
||||
lookup: {
|
||||
attachedTo: training.class.Training
|
||||
}
|
||||
} as FindOptions<TrainingRequest>,
|
||||
/* eslint-enable @typescript-eslint/consistent-type-assertions */
|
||||
configOptions: {
|
||||
strict: true,
|
||||
sortable: true
|
||||
}
|
||||
}
|
||||
|
||||
;(function defineSentRequest () {
|
||||
defineTableBrowserViewletDescriptor(
|
||||
builder,
|
||||
training.icon.ViewSentRequests,
|
||||
training.string.ViewSentRequests,
|
||||
training.viewlet.SentRequests
|
||||
)
|
||||
const sentRequestsViewletData: Data<Viewlet> = {
|
||||
...requestsViewletData,
|
||||
config: [
|
||||
columnSeqNumber,
|
||||
columnTrainingTitle,
|
||||
columnTrainingRevision,
|
||||
columnSentState,
|
||||
columnCompletion,
|
||||
'createdOn',
|
||||
columnMaxAttempts,
|
||||
columnDueDate,
|
||||
columnOwner
|
||||
],
|
||||
descriptor: training.viewlet.SentRequests
|
||||
}
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, sentRequestsViewletData)
|
||||
|
||||
defineTableBrowserViewletDescriptor(
|
||||
builder,
|
||||
training.icon.ViewSentRequests,
|
||||
training.string.ViewSentRequests,
|
||||
training.viewlet.TrainingSentRequests
|
||||
)
|
||||
const trainingSentRequestsViewletData: Data<Viewlet> = {
|
||||
...sentRequestsViewletData,
|
||||
descriptor: training.viewlet.TrainingSentRequests,
|
||||
config: sentRequestsViewletData.config.filter(
|
||||
(column) => column !== columnTrainingTitle && column !== columnTrainingRevision
|
||||
)
|
||||
}
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, trainingSentRequestsViewletData)
|
||||
|
||||
definePermission(builder, training.permission.ChangeSomeoneElsesSentRequestOwner, {
|
||||
label: training.string.Permission_ChangeSomeoneElsesSentRequestOwner,
|
||||
description: training.string.Permission_ChangeSomeoneElsesSentRequestOwner_Description
|
||||
})
|
||||
definePermission(builder, training.permission.CreateRequestOnSomeoneElsesTraining, {
|
||||
label: training.string.Permission_CreateRequestOnSomeoneElsesTraining,
|
||||
description: training.string.Permission_CreateRequestOnSomeoneElsesTraining_Description
|
||||
})
|
||||
definePermission(builder, training.permission.ViewSomeoneElsesSentRequest, {
|
||||
label: training.string.Permission_ViewSomeoneElsesSentRequest,
|
||||
description: training.string.Permission_ViewSomeoneElsesSentRequest_Description
|
||||
})
|
||||
})()
|
||||
;(function defineIncomingRequest () {
|
||||
defineTableBrowserViewletDescriptor(
|
||||
builder,
|
||||
training.icon.ViewIncomingRequests,
|
||||
training.string.ViewIncomingRequests,
|
||||
training.viewlet.IncomingRequests
|
||||
)
|
||||
const incomingRequestsViewletData: Data<Viewlet> = {
|
||||
...requestsViewletData,
|
||||
descriptor: training.viewlet.IncomingRequests,
|
||||
config: [
|
||||
columnSeqNumber,
|
||||
columnTrainingTitle,
|
||||
columnTrainingRevision,
|
||||
columnIncomingAttempts,
|
||||
columnIncomingState,
|
||||
'createdOn',
|
||||
columnDueDate,
|
||||
columnOwner
|
||||
]
|
||||
}
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, incomingRequestsViewletData)
|
||||
|
||||
defineTableBrowserViewletDescriptor(
|
||||
builder,
|
||||
training.icon.ViewIncomingRequests,
|
||||
training.string.ViewIncomingRequests,
|
||||
training.viewlet.TrainingIncomingRequests
|
||||
)
|
||||
const trainingIncomingRequestsViewletData: Data<Viewlet> = {
|
||||
...incomingRequestsViewletData,
|
||||
descriptor: training.viewlet.TrainingIncomingRequests,
|
||||
config: incomingRequestsViewletData.config.filter(
|
||||
(column) => column !== columnTrainingTitle && column !== columnTrainingRevision
|
||||
)
|
||||
}
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, trainingIncomingRequestsViewletData)
|
||||
})()
|
||||
|
||||
builder.mixin(training.class.TrainingRequest, core.class.Class, view.mixin.IgnoreActions, {
|
||||
actions: [view.action.Delete, tracker.action.NewRelatedIssue, print.action.Print]
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: training.action.TrainingRequestChangeOwnerAction,
|
||||
visibilityTester: training.action.TrainingRequestChangeOwnerIsAvailable,
|
||||
input: 'focus',
|
||||
label: training.string.ChangeOwner,
|
||||
icon: contact.icon.Person,
|
||||
target: training.class.TrainingRequest,
|
||||
context: { mode: ['context'], group: 'edit' },
|
||||
category: training.actionCategory.Training
|
||||
},
|
||||
training.action.TrainingRequestChangeOwner
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: training.action.TrainingRequestCancelAction,
|
||||
visibilityTester: training.action.TrainingRequestCancelIsAvailable,
|
||||
input: 'focus',
|
||||
label: training.string.TrainingRequestCancel,
|
||||
icon: training.icon.Cancel,
|
||||
target: training.class.TrainingRequest,
|
||||
context: { mode: ['context'], group: 'remove' },
|
||||
category: training.actionCategory.Training
|
||||
},
|
||||
training.action.TrainingRequestCancel
|
||||
)
|
||||
|
||||
builder.mixin(training.class.TrainingRequest, core.class.Class, activity.mixin.ActivityDoc, {})
|
||||
|
||||
builder.mixin(training.class.TrainingRequest, core.class.Class, view.mixin.ObjectTitle, {
|
||||
titleProvider: training.function.TrainingRequestObjectTitleProvider
|
||||
})
|
||||
|
||||
builder.mixin(training.class.TrainingRequest, core.class.Class, notification.mixin.ClassCollaborators, {
|
||||
fields: ['trainees'] as Array<keyof TrainingRequest>
|
||||
})
|
||||
|
||||
builder.createDoc<NotificationType>(
|
||||
notification.class.NotificationType,
|
||||
core.space.Model,
|
||||
{
|
||||
hidden: false,
|
||||
generated: false,
|
||||
allowedForAuthor: true,
|
||||
label: training.string.TrainingRequest,
|
||||
group: training.notification.TrainingGroup,
|
||||
txClasses: [core.class.TxCreateDoc, core.class.TxUpdateDoc],
|
||||
objectClass: training.class.TrainingRequest,
|
||||
providers: {
|
||||
[notification.providers.EmailNotification]: true,
|
||||
[notification.providers.PlatformNotification]: true
|
||||
},
|
||||
templates: {
|
||||
textTemplate: '{sender} sent you a training request {doc}',
|
||||
htmlTemplate: '<p><b>{sender}</b> sent you a training request {doc}</p>',
|
||||
subjectTemplate: 'Training request {doc}'
|
||||
}
|
||||
},
|
||||
training.notification.TrainingRequest
|
||||
)
|
||||
}
|
||||
|
||||
function defineTrainingAttempt (builder: Builder): void {
|
||||
builder.createModel(TTrainingAttemptState)
|
||||
|
||||
builder.mixin(training.class.TypeTrainingAttemptState, core.class.Class, view.mixin.SortFuncs, {
|
||||
func: training.function.TrainingAttemptStateSort
|
||||
})
|
||||
|
||||
builder.mixin(training.class.TypeTrainingAttemptState, core.class.Class, view.mixin.AllValuesFunc, {
|
||||
func: training.function.TrainingAttemptStateAllValues
|
||||
})
|
||||
|
||||
builder.mixin(training.class.TypeTrainingAttemptState, core.class.Class, view.mixin.AttributeFilterPresenter, {
|
||||
presenter: training.component.TrainingAttemptStateFilterPresenter
|
||||
})
|
||||
|
||||
builder.mixin(training.class.TypeTrainingAttemptState, core.class.Class, view.mixin.AttributeFilter, {
|
||||
component: view.component.ValueFilter
|
||||
})
|
||||
|
||||
classPresenter(builder, training.class.TypeTrainingAttemptState, training.component.TrainingAttemptStatePresenter)
|
||||
|
||||
builder.createModel(TTrainingAttempt)
|
||||
|
||||
builder.mixin(training.class.TrainingAttempt, core.class.Class, view.mixin.LinkProvider, {
|
||||
encode: training.function.TrainingAttemptLinkProviderEncode
|
||||
})
|
||||
|
||||
builder.mixin(training.class.TrainingAttempt, core.class.Class, view.mixin.ObjectPanel, {
|
||||
component: training.component.TrainingAttemptPanel
|
||||
})
|
||||
|
||||
const columnTrainingSeqNumber: BuildModelKey = {
|
||||
key: '',
|
||||
sortingKey: '$lookup.attachedTo.$lookup.attachedTo.seqNumber',
|
||||
label: training.string.Training,
|
||||
presenter: training.component.TrainingAttemptPresenter
|
||||
}
|
||||
const columnTrainingTitle: BuildModelKey = {
|
||||
...columns.trainingTitle,
|
||||
key: '$lookup.attachedTo.$lookup.attachedTo.title'
|
||||
}
|
||||
const columnTrainingRevision: BuildModelKey = {
|
||||
...columns.trainingRevision,
|
||||
key: '$lookup.attachedTo.$lookup.attachedTo.revision',
|
||||
sortingKey: '$lookup.attachedTo.$lookup.attachedTo.revision'
|
||||
}
|
||||
const columnAttemptSeqNumber: BuildModelKey = {
|
||||
key: '',
|
||||
sortingKey: 'seqNumber',
|
||||
label: training.string.TrainingAttempt,
|
||||
presenter: training.component.TrainingAttemptNumberPresenter,
|
||||
displayProps: { align: 'center' }
|
||||
}
|
||||
const columnScore: BuildModelKey = {
|
||||
key: '',
|
||||
sortingKey: 'score',
|
||||
presenter: training.component.TrainingAttemptScorePresenter,
|
||||
label: questions.string.Score
|
||||
}
|
||||
const columnState: BuildModelKey = {
|
||||
key: 'state',
|
||||
sortingKey: 'state',
|
||||
displayProps: { align: 'center' }
|
||||
}
|
||||
const columnOwner: BuildModelKey = {
|
||||
...columns.owner,
|
||||
key: 'owner'
|
||||
}
|
||||
|
||||
defineTableBrowserViewletDescriptor(
|
||||
builder,
|
||||
training.icon.TrainingAttempt,
|
||||
training.string.TrainingAttempts,
|
||||
training.viewlet.TrainingAttempts
|
||||
)
|
||||
const trainingAttemptsViewletData: Omit<Data<Viewlet>, 'descriptor'> = {
|
||||
attachTo: training.class.TrainingAttempt,
|
||||
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
||||
options: {
|
||||
lookup: {
|
||||
attachedTo: [
|
||||
training.class.TrainingRequest,
|
||||
{
|
||||
attachedTo: training.class.Training
|
||||
}
|
||||
]
|
||||
}
|
||||
} as FindOptions<TrainingAttempt>,
|
||||
/* eslint-enable @typescript-eslint/consistent-type-assertions */
|
||||
config: [
|
||||
columnTrainingSeqNumber,
|
||||
columnTrainingTitle,
|
||||
columnTrainingRevision,
|
||||
columnAttemptSeqNumber,
|
||||
columnState,
|
||||
columnScore,
|
||||
'createdOn',
|
||||
'submittedOn',
|
||||
columnOwner
|
||||
],
|
||||
configOptions: {
|
||||
strict: true,
|
||||
sortable: true
|
||||
}
|
||||
}
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
...trainingAttemptsViewletData,
|
||||
descriptor: training.viewlet.TrainingAttempts
|
||||
})
|
||||
|
||||
defineTableBrowserViewletDescriptor(
|
||||
builder,
|
||||
training.icon.TrainingAttempt,
|
||||
training.string.TrainingAttempts,
|
||||
training.viewlet.TrainingAttemptsNested
|
||||
)
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
...trainingAttemptsViewletData,
|
||||
descriptor: training.viewlet.TrainingAttemptsNested,
|
||||
config: trainingAttemptsViewletData.config.filter(
|
||||
(column) => column !== columnTrainingTitle && column !== columnTrainingRevision
|
||||
)
|
||||
})
|
||||
|
||||
builder.mixin(training.class.TrainingAttempt, core.class.Class, view.mixin.ClassFilters, {
|
||||
filters: ['state', 'owner', 'submittedOn'] as Array<keyof TrainingAttempt>,
|
||||
strict: true
|
||||
})
|
||||
|
||||
builder.mixin(training.class.TrainingAttempt, core.class.Class, view.mixin.IgnoreActions, {
|
||||
actions: [view.action.Delete, tracker.action.NewRelatedIssue, print.action.Print]
|
||||
})
|
||||
|
||||
definePermission(builder, training.permission.ViewSomeoneElsesTraineesResults, {
|
||||
label: training.string.Permission_ViewSomeoneElsesTraineesResults,
|
||||
description: training.string.Permission_ViewSomeoneElsesTraineesResults_Description
|
||||
})
|
||||
}
|
||||
|
||||
function defineApplication (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
workbench.class.Application,
|
||||
core.space.Model,
|
||||
{
|
||||
label: training.string.TrainingApplication,
|
||||
icon: training.icon.TrainingApplication,
|
||||
alias: trainingId,
|
||||
hidden: false,
|
||||
locationResolver: training.resolver.Location,
|
||||
navigatorModel: {
|
||||
spaces: [],
|
||||
specials: [
|
||||
{
|
||||
id: TrainingSpecialIds.AllTrainings,
|
||||
icon: training.icon.ViewAllTrainings,
|
||||
label: training.string.ViewAllTrainings,
|
||||
position: '00:common',
|
||||
component: training.component.ViewAllTrainings,
|
||||
componentProps: {
|
||||
_class: training.class.Training,
|
||||
icon: training.icon.ViewAllTrainings,
|
||||
label: training.string.ViewAllTrainings
|
||||
}
|
||||
},
|
||||
{
|
||||
id: TrainingSpecialIds.IncomingRequests,
|
||||
icon: training.icon.ViewIncomingRequests,
|
||||
label: training.string.ViewIncomingRequests,
|
||||
position: '10:trainee',
|
||||
component: training.component.ViewIncomingRequests,
|
||||
componentProps: {
|
||||
_class: training.class.TrainingRequest,
|
||||
icon: training.icon.ViewIncomingRequests,
|
||||
label: training.string.ViewIncomingRequests,
|
||||
descriptors: [training.viewlet.IncomingRequests]
|
||||
}
|
||||
},
|
||||
{
|
||||
id: TrainingSpecialIds.MyResults,
|
||||
icon: training.icon.ViewMyResults,
|
||||
label: training.string.ViewMyResults,
|
||||
position: '10:trainee',
|
||||
component: training.component.ViewMyResults,
|
||||
componentProps: {
|
||||
_class: training.class.TrainingAttempt,
|
||||
icon: training.icon.ViewMyResults,
|
||||
label: training.string.ViewMyResults,
|
||||
descriptors: [training.viewlet.TrainingAttempts]
|
||||
}
|
||||
},
|
||||
{
|
||||
id: TrainingSpecialIds.MyTrainings,
|
||||
icon: training.icon.ViewMyTrainings,
|
||||
label: training.string.ViewMyTrainings,
|
||||
position: '20:trainer',
|
||||
component: training.component.ViewMyTrainings,
|
||||
componentProps: {
|
||||
_class: training.class.Training,
|
||||
icon: training.icon.ViewMyTrainings,
|
||||
label: training.string.ViewMyTrainings
|
||||
}
|
||||
},
|
||||
{
|
||||
id: TrainingSpecialIds.SentRequests,
|
||||
icon: training.icon.ViewSentRequests,
|
||||
label: training.string.ViewSentRequests,
|
||||
position: '20:trainer',
|
||||
component: training.component.ViewSentRequests,
|
||||
componentProps: {
|
||||
_class: training.class.TrainingRequest,
|
||||
icon: training.icon.ViewSentRequests,
|
||||
label: training.string.ViewSentRequests,
|
||||
descriptors: [training.viewlet.SentRequests]
|
||||
}
|
||||
},
|
||||
{
|
||||
id: TrainingSpecialIds.TraineesResults,
|
||||
icon: training.icon.TrainingAttempt,
|
||||
label: training.string.ViewTraineesResults,
|
||||
position: '20:trainer',
|
||||
component: training.component.ViewTraineesResults,
|
||||
componentProps: {
|
||||
_class: training.class.TrainingAttempt,
|
||||
icon: training.icon.TrainingAttempt,
|
||||
label: training.string.ViewTraineesResults,
|
||||
descriptors: [training.viewlet.TrainingAttempts]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
training.app.Training
|
||||
)
|
||||
}
|
||||
|
||||
function definePermission (builder: Builder, id: Ref<Permission>, data: Data<Permission>): void {
|
||||
builder.createDoc(core.class.Permission, core.space.Model, data, id)
|
||||
}
|
||||
|
||||
function defineTableBrowserViewletDescriptor (
|
||||
builder: Builder,
|
||||
icon: Asset,
|
||||
label: IntlString,
|
||||
ref: Ref<ViewletDescriptor>
|
||||
): void {
|
||||
builder.createDoc(
|
||||
view.class.ViewletDescriptor,
|
||||
core.space.Model,
|
||||
{
|
||||
icon,
|
||||
label,
|
||||
component: view.component.TableBrowser
|
||||
},
|
||||
ref
|
||||
)
|
||||
}
|
||||
|
||||
function defineSettings (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
setting.class.WorkspaceSettingCategory,
|
||||
core.space.Model,
|
||||
{
|
||||
name: 'trainings',
|
||||
label: training.string.Trainings,
|
||||
icon: training.icon.Training,
|
||||
component: training.component.Settings,
|
||||
order: 1150,
|
||||
role: AccountRole.Maintainer
|
||||
},
|
||||
training.setting.Trainings
|
||||
)
|
||||
}
|
||||
|
||||
const columns = {
|
||||
trainingTitle: {
|
||||
key: 'title',
|
||||
label: training.string.TrainingTitle,
|
||||
props: { accent: true },
|
||||
displayProps: { grow: true, align: 'left' }
|
||||
},
|
||||
trainingRevision: {
|
||||
key: 'revision',
|
||||
label: training.string.Revision,
|
||||
presenter: view.component.NumberPresenter,
|
||||
displayProps: { align: 'center' }
|
||||
},
|
||||
owner: {
|
||||
key: 'owner',
|
||||
label: training.string.Owner,
|
||||
presenter: contacts.component.EmployeePresenter,
|
||||
props: { shouldShowName: true },
|
||||
displayProps: { align: 'center' }
|
||||
}
|
||||
} as const
|
||||
@@ -0,0 +1,117 @@
|
||||
//
|
||||
// Copyright © 2024 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.
|
||||
//
|
||||
|
||||
import {
|
||||
tryMigrate,
|
||||
type MigrateOperation,
|
||||
type MigrationClient,
|
||||
type MigrationUpgradeClient
|
||||
} from '@hcengineering/model'
|
||||
import { type Ref, TxOperations, type TypedSpace } from '@hcengineering/core'
|
||||
import core, { DOMAIN_SPACE } from '@hcengineering/model-core'
|
||||
import training, { trainingId, type Sequence } from '@hcengineering/training'
|
||||
import contact, { DOMAIN_CONTACT } from '@hcengineering/model-contact'
|
||||
import { Person, PersonAccount } from '@hcengineering/contact'
|
||||
|
||||
export const trainingOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {
|
||||
await tryMigrate(client, trainingId, [
|
||||
{
|
||||
state: 'migrateDefaultSpaceMembers',
|
||||
func: migrateDefaultSpaceMembers
|
||||
}
|
||||
])
|
||||
},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||
const tx = new TxOperations(client, core.account.System)
|
||||
await ensureTypedSpace(tx)
|
||||
await ensureSequence(tx)
|
||||
}
|
||||
}
|
||||
|
||||
async function migrateDefaultSpaceMembers (client: MigrationClient): Promise<void> {
|
||||
await client.update(
|
||||
DOMAIN_SPACE,
|
||||
{
|
||||
_id: training.space.Trainings,
|
||||
autoJoin: false
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
autoJoin: true
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const employees = await client.find<Person>(DOMAIN_CONTACT, {
|
||||
_class: contact.class.Person,
|
||||
[contact.mixin.Employee]: { $exists: true }
|
||||
})
|
||||
const empAccs = await client.model.findAll<PersonAccount>(contact.class.PersonAccount, {
|
||||
person: { $in: employees.map((e) => e._id) }
|
||||
})
|
||||
|
||||
await client.update(
|
||||
DOMAIN_SPACE,
|
||||
{
|
||||
_id: training.space.Trainings
|
||||
},
|
||||
{
|
||||
$addToSet: {
|
||||
members: { $each: empAccs.map((e) => e._id) }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function ensureTypedSpace (tx: TxOperations): Promise<Ref<TypedSpace>> {
|
||||
const existing = await tx.findOne(core.class.TypedSpace, {
|
||||
_id: training.space.Trainings
|
||||
})
|
||||
|
||||
if (existing !== undefined) {
|
||||
return existing._id
|
||||
}
|
||||
|
||||
return await tx.createDoc(
|
||||
core.class.TypedSpace,
|
||||
core.space.Space,
|
||||
{
|
||||
name: 'Trainings',
|
||||
description: 'Space for all trainings',
|
||||
private: false,
|
||||
archived: false,
|
||||
autoJoin: true,
|
||||
members: [],
|
||||
type: training.spaceType.Trainings
|
||||
},
|
||||
training.space.Trainings
|
||||
)
|
||||
}
|
||||
|
||||
async function ensureSequence (tx: TxOperations): Promise<Ref<Sequence>> {
|
||||
const existing = await tx.findOne(training.class.Sequence, {
|
||||
attachedTo: training.class.Training
|
||||
})
|
||||
|
||||
if (existing !== undefined) {
|
||||
return existing._id
|
||||
}
|
||||
|
||||
return await tx.createDoc(training.class.Sequence, training.space.Trainings, {
|
||||
attachedTo: training.class.Training,
|
||||
sequence: 0
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// Copyright © 2024 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.
|
||||
//
|
||||
|
||||
import type { NotificationGroup, NotificationType } from '@hcengineering/notification'
|
||||
import { trainingId } from '@hcengineering/training'
|
||||
import training from '@hcengineering/training-resources/src/plugin'
|
||||
import { mergeIds, type Resource } from '@hcengineering/platform'
|
||||
import { type Doc, type Ref } from '@hcengineering/core'
|
||||
import { type Location, type ResolvedLocation } from '@hcengineering/ui'
|
||||
import { type Application } from '@hcengineering/workbench'
|
||||
import { type ActionCategory } from '@hcengineering/view'
|
||||
|
||||
export default mergeIds(trainingId, training, {
|
||||
app: {
|
||||
Training: '' as Ref<Application>
|
||||
},
|
||||
actionCategory: {
|
||||
Training: '' as Ref<ActionCategory>
|
||||
},
|
||||
notification: {
|
||||
TrainingGroup: '' as Ref<NotificationGroup>,
|
||||
TrainingRequest: '' as Ref<NotificationType>
|
||||
},
|
||||
|
||||
// TODO: Move function resources declarations to plugins/*-resources
|
||||
// Currently, dependencies look like this:
|
||||
// models/*/src/index.ts
|
||||
// → models/*/src/plugin.ts
|
||||
// → plugins/*-resources/src/plugin.ts
|
||||
// → plugins/*/src/plugin.ts
|
||||
// For unknown reasons in plugins/*-resources we can declare component resources that depend on Svelte,
|
||||
// but not function (incl. location resolver) resources that depend on UI (particularly on Location and ResolvedLocation).
|
||||
// The build fails saying:
|
||||
// > The inferred type of 'default' cannot be named without a reference to '[..]/node_modules/@hcengineering/ui/src'.
|
||||
// > This is likely not portable. A type annotation is necessary.
|
||||
|
||||
function: {
|
||||
TrainingAttemptLinkProviderEncode: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
|
||||
TrainingLinkProviderEncode: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
|
||||
TrainingRequestLinkProviderEncode: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>
|
||||
},
|
||||
resolver: {
|
||||
Location: '' as Resource<(location: Location) => Promise<ResolvedLocation | undefined>>
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// Copyright © 2024 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.
|
||||
//
|
||||
import training from './plugin'
|
||||
|
||||
export const roles = [
|
||||
{
|
||||
_id: training.role.QARA,
|
||||
name: 'QARA',
|
||||
permissions: [
|
||||
training.permission.CreateTraining,
|
||||
training.permission.ChangeSomeoneElsesTrainingOwner,
|
||||
training.permission.ViewSomeoneElsesTrainingOverview,
|
||||
training.permission.ViewSomeoneElsesTrainingQuestions,
|
||||
training.permission.CreateRequestOnSomeoneElsesTraining,
|
||||
training.permission.ChangeSomeoneElsesSentRequestOwner,
|
||||
training.permission.ViewSomeoneElsesSentRequest,
|
||||
training.permission.ViewSomeoneElsesTraineesResults
|
||||
]
|
||||
},
|
||||
{
|
||||
_id: training.role.Manager,
|
||||
name: 'Managers',
|
||||
permissions: [
|
||||
training.permission.CreateTraining,
|
||||
training.permission.ViewSomeoneElsesTrainingOverview,
|
||||
training.permission.ViewSomeoneElsesTrainingQuestions,
|
||||
training.permission.CreateRequestOnSomeoneElsesTraining,
|
||||
training.permission.ViewSomeoneElsesSentRequest,
|
||||
training.permission.ViewSomeoneElsesTraineesResults
|
||||
]
|
||||
},
|
||||
{
|
||||
_id: training.role.QualifiedUser,
|
||||
name: 'Qualified Users',
|
||||
permissions: [training.permission.CreateTraining]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,277 @@
|
||||
//
|
||||
// Copyright © 2024 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.
|
||||
//
|
||||
|
||||
import type { Attachment } from '@hcengineering/attachment'
|
||||
import attachments from '@hcengineering/model-attachment'
|
||||
import questions, { TypePercentage } from '@hcengineering/model-questions'
|
||||
import type { Answer, Percentage, Question } from '@hcengineering/questions'
|
||||
import {
|
||||
type Sequence,
|
||||
type Training,
|
||||
type TrainingAttempt,
|
||||
TrainingAttemptState,
|
||||
type TrainingRequest,
|
||||
TrainingState
|
||||
} from '@hcengineering/training'
|
||||
import contact, { type Employee } from '@hcengineering/contact'
|
||||
import core, {
|
||||
type Class,
|
||||
type CollectionSize,
|
||||
type Doc,
|
||||
type Domain,
|
||||
IndexKind,
|
||||
type Markup,
|
||||
type Ref,
|
||||
type Timestamp,
|
||||
type Type,
|
||||
type TypedSpace,
|
||||
RolesAssignment,
|
||||
Role,
|
||||
Account
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
ArrOf,
|
||||
Collection,
|
||||
Hidden,
|
||||
Index,
|
||||
Mixin,
|
||||
Model,
|
||||
Prop,
|
||||
TypeMarkup,
|
||||
TypeNumber,
|
||||
TypeRef,
|
||||
TypeString,
|
||||
TypeTimestamp,
|
||||
UX
|
||||
} from '@hcengineering/model'
|
||||
import { TAttachedDoc, TDoc, TType, TTypedSpace } from '@hcengineering/model-core'
|
||||
import training from './plugin'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
|
||||
export const DOMAIN_TRAINING = 'training' as Domain
|
||||
|
||||
/** @public */
|
||||
export function TypeTrainingState (): Type<TrainingState> {
|
||||
return { _class: training.class.TypeTrainingState, label: training.string.State }
|
||||
}
|
||||
|
||||
@UX(training.string.State)
|
||||
@Model(training.class.TypeTrainingState, core.class.Type)
|
||||
export class TTrainingState extends TType {}
|
||||
|
||||
/** @public */
|
||||
export function TypeTrainingAttemptState (): Type<TrainingAttemptState> {
|
||||
return { _class: training.class.TypeTrainingAttemptState, label: training.string.State }
|
||||
}
|
||||
|
||||
@UX(training.string.State)
|
||||
@Model(training.class.TypeTrainingAttemptState, core.class.Type)
|
||||
export class TTrainingAttemptState extends TType {}
|
||||
|
||||
/** @public */
|
||||
@Model(training.class.Training, core.class.Doc, DOMAIN_TRAINING)
|
||||
@UX(training.string.Training, training.icon.Training)
|
||||
export class TTraining extends TDoc implements Training {
|
||||
@Prop(TypeRef(core.class.TypedSpace), core.string.Space)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare space: Ref<TypedSpace>
|
||||
|
||||
@Prop(TypeString(), training.string.TrainingTitle)
|
||||
@Index(IndexKind.FullText)
|
||||
title!: string
|
||||
|
||||
@Prop(TypeString(), core.string.Id)
|
||||
@Index(IndexKind.FullText)
|
||||
code!: string
|
||||
|
||||
@Prop(TypeNumber(), training.string.Revision)
|
||||
@Index(IndexKind.Indexed)
|
||||
revision!: number
|
||||
|
||||
@Prop(TypeRef(contact.mixin.Employee), training.string.TrainingAuthor, {
|
||||
editor: training.component.EmployeeEditor
|
||||
})
|
||||
author!: Ref<Employee>
|
||||
|
||||
@Prop(TypeRef(contact.mixin.Employee), training.string.Owner, {
|
||||
editor: training.component.EmployeeEditor
|
||||
})
|
||||
owner!: Ref<Employee>
|
||||
|
||||
@Prop(TypeTrainingState(), training.string.State)
|
||||
state!: TrainingState
|
||||
|
||||
@Prop(TypeMarkup(), core.string.Description, { defaultValue: 0 })
|
||||
@Index(IndexKind.Indexed)
|
||||
description: Markup = ''
|
||||
|
||||
@Prop(Collection(attachments.class.Attachment), attachments.string.Attachments, { defaultValue: 0 })
|
||||
attachments: CollectionSize<Attachment> = 0
|
||||
|
||||
@Prop(TypePercentage(), training.string.TrainingPassingScore, { defaultValue: 100 })
|
||||
@Index(IndexKind.Indexed)
|
||||
passingScore: Percentage = 100
|
||||
|
||||
@Prop(TypeTimestamp(), training.string.TrainingReleasedOn, { defaultValue: null })
|
||||
@Index(IndexKind.Indexed)
|
||||
releasedOn: Timestamp | null = null
|
||||
|
||||
@Prop(TypeRef(contact.mixin.Employee), training.string.TrainingReleasedBy, {
|
||||
defaultValue: null,
|
||||
editor: training.component.EmployeeEditor
|
||||
})
|
||||
releasedBy: Ref<Employee> | null = null
|
||||
|
||||
@Prop(Collection(questions.class.Question), training.string.TrainingQuestions, { defaultValue: 0 })
|
||||
questions: CollectionSize<Question<any>> = 0
|
||||
|
||||
@Prop(Collection(training.class.TrainingRequest), training.string.TrainingRequests, { defaultValue: 0 })
|
||||
requests: CollectionSize<TrainingRequest> = 0
|
||||
}
|
||||
|
||||
@Model(training.class.TrainingRequest, core.class.AttachedDoc, DOMAIN_TRAINING)
|
||||
@UX(training.string.TrainingRequest, training.icon.TrainingRequest)
|
||||
export class TTrainingRequest extends TAttachedDoc implements TrainingRequest {
|
||||
@Prop(TypeRef(core.class.TypedSpace), core.string.Space)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare space: Ref<TypedSpace>
|
||||
|
||||
@Prop(TypeRef(training.class.Training), core.string.AttachedTo)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare attachedTo: Ref<Training>
|
||||
|
||||
@Prop(TypeRef(training.class.Training), core.string.AttachedToClass)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare attachedToClass: Ref<Class<Training>>
|
||||
|
||||
@Prop(TypeString(), core.string.Collection)
|
||||
@Hidden()
|
||||
override collection: 'requests' = 'requests'
|
||||
|
||||
@Prop(TypeRef(contact.mixin.Employee), training.string.Owner, {
|
||||
editor: training.component.EmployeeEditor
|
||||
})
|
||||
@Index(IndexKind.Indexed)
|
||||
owner!: Ref<Employee>
|
||||
|
||||
@Prop(ArrOf(TypeRef(contact.mixin.Employee)), training.string.TrainingRequestTrainees, {
|
||||
editor: training.component.TrainingRequestTraineesEditor
|
||||
})
|
||||
trainees: Ref<Employee>[] = []
|
||||
|
||||
@Prop(TypeTimestamp(), training.string.TrainingRequestDueDate, {
|
||||
defaultValue: null,
|
||||
editor: training.component.TrainingRequestDueDateEditor
|
||||
})
|
||||
@Index(IndexKind.Indexed)
|
||||
dueDate: Timestamp | null = null
|
||||
|
||||
@Prop(TypeNumber(), training.string.TrainingRequestMaxAttempts, {
|
||||
defaultValue: null,
|
||||
editor: training.component.TrainingRequestMaxAttemptsEditor
|
||||
})
|
||||
maxAttempts: number | null = null
|
||||
|
||||
@Prop(Collection(training.class.TrainingAttempt), training.string.TrainingAttempts, { defaultValue: 0 })
|
||||
attempts: CollectionSize<TrainingAttempt> = 0
|
||||
|
||||
@Prop(TypeTimestamp(), training.string.TrainingRequestCanceledDate, { defaultValue: null })
|
||||
canceledOn: Timestamp | null = null
|
||||
|
||||
@Prop(TypeRef(contact.mixin.Employee), training.string.TrainingRequestCanceledBy, {
|
||||
defaultValue: null,
|
||||
editor: training.component.EmployeeEditor
|
||||
})
|
||||
canceledBy: Ref<Employee> | null = null
|
||||
}
|
||||
|
||||
@Model(training.class.TrainingAttempt, core.class.AttachedDoc, DOMAIN_TRAINING)
|
||||
@UX(training.string.TrainingAttempt, training.icon.TrainingAttempt)
|
||||
export class TTrainingAttempt extends TAttachedDoc implements TrainingAttempt {
|
||||
@Prop(TypeRef(core.class.TypedSpace), core.string.Space)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare space: Ref<TypedSpace>
|
||||
|
||||
@Prop(TypeRef(training.class.TrainingRequest), core.string.AttachedTo)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare attachedTo: Ref<TrainingRequest>
|
||||
|
||||
@Prop(TypeRef(training.class.TrainingRequest), core.string.AttachedToClass)
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
declare attachedToClass: Ref<Class<TrainingRequest>>
|
||||
|
||||
@Prop(TypeString(), core.string.Collection)
|
||||
@Hidden()
|
||||
override collection: 'attempts' = 'attempts'
|
||||
|
||||
@Prop(TypeRef(contact.mixin.Employee), training.string.Owner, {
|
||||
editor: training.component.EmployeeEditor
|
||||
})
|
||||
@Index(IndexKind.Indexed)
|
||||
owner!: Ref<Employee>
|
||||
|
||||
@Prop(TypeNumber(), training.string.TrainingAttempt, { defaultValue: 1 })
|
||||
@Index(IndexKind.Indexed)
|
||||
seqNumber: number = 1
|
||||
|
||||
@Prop(TypeTrainingAttemptState(), training.string.State)
|
||||
@Index(IndexKind.Indexed)
|
||||
state: TrainingAttemptState = TrainingAttemptState.Draft
|
||||
|
||||
@Prop(Collection(questions.class.Answer), questions.string.Answers, { defaultValue: 0 })
|
||||
answers: CollectionSize<Answer<any, any>> = 0
|
||||
|
||||
@Prop(TypeTimestamp(), training.string.TrainingAttemptSubmittedDate, { defaultValue: null })
|
||||
@Index(IndexKind.Indexed)
|
||||
submittedOn: Timestamp | null = null
|
||||
|
||||
@Prop(TypeRef(contact.mixin.Employee), training.string.TrainingAttemptSubmittedBy, {
|
||||
defaultValue: null,
|
||||
editor: training.component.EmployeeEditor
|
||||
})
|
||||
submittedBy: Ref<Employee> | null = null
|
||||
|
||||
@Prop(TypePercentage(), questions.string.Score, { defaultValue: null })
|
||||
@Index(IndexKind.Indexed)
|
||||
score: Percentage | null = null
|
||||
|
||||
@Prop(TypeNumber(), training.string.TrainingAttemptAssessmentsTotal, { defaultValue: null })
|
||||
assessmentsTotal: number | null = null
|
||||
|
||||
@Prop(TypeNumber(), training.string.TrainingAttemptAssessmentsPassed, { defaultValue: null })
|
||||
assessmentsPassed: number | null = null
|
||||
}
|
||||
|
||||
@Model(training.class.Sequence, core.class.Doc, DOMAIN_TRAINING)
|
||||
export class TSequence extends TDoc implements Sequence {
|
||||
@Prop(TypeRef(core.class.Doc), core.string.AttachedTo)
|
||||
@Index(IndexKind.Indexed)
|
||||
attachedTo!: Ref<Class<Doc>>
|
||||
|
||||
sequence!: number
|
||||
}
|
||||
|
||||
@Mixin(training.mixin.TrainingsTypeData, core.class.TypedSpace)
|
||||
@UX(getEmbeddedLabel('Default Trainings'), training.icon.TrainingApplication)
|
||||
export class TTrainingsTypeData extends TTypedSpace implements RolesAssignment {
|
||||
[key: Ref<Role>]: Ref<Account>[]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/assets/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||
<symbol id="checkmark-circle" viewBox="0 0 20 20">
|
||||
<path d="M10 1.25C8.26942 1.25 6.57769 1.76318 5.13876 2.72464C3.69983 3.6861 2.57832 5.05267 1.91606 6.65152C1.25379 8.25037 1.08051 10.0097 1.41813 11.707C1.75575 13.4044 2.58911 14.9635 3.81282 16.1872C5.03653 17.4109 6.59563 18.2442 8.29296 18.5819C9.9903 18.9195 11.7496 18.7462 13.3485 18.0839C14.9473 17.4217 16.3139 16.3002 17.2754 14.8612C18.2368 13.4223 18.75 11.7306 18.75 10C18.75 7.67936 17.8281 5.45376 16.1872 3.81282C14.5462 2.17187 12.3206 1.25 10 1.25ZM10 17.5C8.51664 17.5 7.0666 17.0601 5.83323 16.236C4.59986 15.4119 3.63856 14.2406 3.07091 12.8701C2.50325 11.4997 2.35473 9.99168 2.64411 8.53682C2.9335 7.08197 3.64781 5.74559 4.6967 4.6967C5.7456 3.64781 7.08197 2.9335 8.53683 2.64411C9.99168 2.35472 11.4997 2.50325 12.8701 3.0709C14.2406 3.63856 15.4119 4.59985 16.236 5.83322C17.0601 7.06659 17.5 8.51664 17.5 10C17.5 11.9891 16.7098 13.8968 15.3033 15.3033C13.8968 16.7098 11.9891 17.5 10 17.5Z" fill="black" fill-opacity="0.8"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.1919 7.05821C14.436 7.30229 14.436 7.69802 14.1919 7.94209L9.19194 12.9421C8.94786 13.1862 8.55214 13.1862 8.30806 12.9421L5.80806 10.4421C5.56398 10.198 5.56398 9.80229 5.80806 9.55821C6.05214 9.31413 6.44786 9.31413 6.69194 9.55821L8.75 11.6163L13.3081 7.05821C13.5521 6.81413 13.9479 6.81413 14.1919 7.05821Z" fill="black" fill-opacity="0.8"/>
|
||||
</symbol>
|
||||
<symbol id="documentapplication" viewBox="0 0 32 32">
|
||||
<path d="M10 14C9.44772 14 9 14.4477 9 15C9 15.5523 9.44772 16 10 16H22C22.5523 16 23 15.5523 23 15C23 14.4477 22.5523 14 22 14H10Z"/>
|
||||
<path d="M9 19C9 18.4477 9.44772 18 10 18H18C18.5523 18 19 18.4477 19 19C19 19.5523 18.5523 20 18 20H10C9.44772 20 9 19.5523 9 19Z"/>
|
||||
<path d="M10 22C9.44772 22 9 22.4477 9 23C9 23.5523 9.44772 24 10 24H16C16.5523 24 17 23.5523 17 23C17 22.4477 16.5523 22 16 22H10Z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19 2C20.3062 2 21.4175 2.83481 21.8293 4H23C25.2091 4 27 5.79086 27 8V26C27 28.2091 25.2091 30 23 30H9C6.79086 30 5 28.2091 5 26V8C5 5.79086 6.79086 4 9 4H10.1707C10.5825 2.83481 11.6938 2 13 2H19ZM12 5C12 4.44772 12.4477 4 13 4H19C19.5523 4 20 4.44772 20 5C20 5.55228 19.5523 6 19 6H13C12.4477 6 12 5.55228 12 5ZM19 8C20.3062 8 21.4175 7.16519 21.8293 6H23C24.1046 6 25 6.89543 25 8V26C25 27.1046 24.1046 28 23 28H9C7.89543 28 7 27.1046 7 26V8C7 6.89543 7.89543 6 9 6H10.1707C10.5825 7.16519 11.6938 8 13 8H19Z"/>
|
||||
</symbol>
|
||||
<symbol id="newdocument" viewBox="0 0 16 16">
|
||||
<path d="M14.7826 3.26359C15.1313 2.69123 15.0606 1.93115 14.5705 1.43492C14.0757 0.933932 13.3153 0.865765 12.7483 1.23041C13.2123 2.09277 13.9198 2.79999 14.7826 3.26359Z"/>
|
||||
<path d="M11.8171 2.11829L6.78472 7.18C7.77457 7.47517 8.57699 8.21908 8.95006 9.18198L13.9064 4.20208C13.0535 3.68436 12.3369 2.9696 11.8171 2.11829Z"/>
|
||||
<path d="M7.91486 10.1761C7.80538 9.1186 6.91913 8.30487 5.8592 8.29183C5.53827 8.92804 5.21105 9.90848 5.01729 10.5311C4.93355 10.8002 5.16675 11.0527 5.44262 10.9905C6.16831 10.8268 7.36057 10.5217 7.91486 10.1761Z"/>
|
||||
<path d="M4.75 2C2.67893 2 1 3.67893 1 5.75V11.25C1 13.3211 2.67893 15 4.75 15H10.2501C12.3212 15 14.0001 13.3211 14.0001 11.25V8C14.0001 7.58579 13.6643 7.25 13.2501 7.25C12.8359 7.25 12.5001 7.58579 12.5001 8V11.25C12.5001 12.4926 11.4927 13.5 10.2501 13.5H4.75C3.50736 13.5 2.5 12.4926 2.5 11.25V5.75C2.5 4.50736 3.50736 3.5 4.75 3.5H7C7.41421 3.5 7.75 3.16421 7.75 2.75C7.75 2.33579 7.41421 2 7 2H4.75Z"/>
|
||||
</symbol>
|
||||
<symbol id="document" viewBox="0 0 24 24">
|
||||
<path d="M21.2,7.9c0-0.1,0-0.1,0-0.2c0-0.1-0.1-0.1-0.1-0.1c0,0,0-0.1-0.1-0.1l-5.5-6c0,0,0,0,0,0c0,0-0.1-0.1-0.1-0.1c0,0-0.1-0.1-0.1-0.1c0,0-0.1,0-0.1,0c-0.1,0-0.1,0-0.2,0c0,0,0,0,0,0H6.5C6.1,1.2,5.8,1.6,5.8,2v17c0,0.4,0.3,0.8,0.8,0.8h14c0.4,0,0.8-0.3,0.8-0.8V8C21.2,8,21.2,7.9,21.2,7.9z M15.8,3.9l3,3.3h-3V3.9z M19.8,18.2H7.2V2.8h7V8c0,0.4,0.3,0.8,0.8,0.8h4.8V18.2z"/>
|
||||
<path d="M14,21.2H4.2V10c0-0.4-0.3-0.8-0.8-0.8S2.8,9.6,2.8,10v12c0,0.4,0.3,0.8,0.8,0.8H14c0.4,0,0.8-0.3,0.8-0.8C14.8,21.6,14.4,21.2,14,21.2z"/>
|
||||
<path d="M9.5,10.8h2c0.4,0,0.8-0.3,0.8-0.8c0-0.4-0.3-0.8-0.8-0.8h-2c-0.4,0-0.8,0.3-0.8,0.8C8.8,10.4,9.1,10.8,9.5,10.8z"/>
|
||||
<path d="M8.8,14c0,0.4,0.3,0.8,0.8,0.8h6c0.4,0,0.8-0.3,0.8-0.8c0-0.4-0.3-0.8-0.8-0.8h-6C9.1,13.2,8.8,13.6,8.8,14z"/>
|
||||
</symbol>
|
||||
<symbol id="library" viewBox="0 0 24 24">
|
||||
<path d="M21,2.2h-5h-5c-0.4,0-0.8,0.3-0.8,0.8v18c0,0.4,0.3,0.8,0.8,0.8h5h5c0.4,0,0.8-0.3,0.8-0.8V3C21.8,2.6,21.4,2.2,21,2.2z M11.8,3.8h3.5v16.5h-3.5V3.8z M20.2,20.2h-3.5V3.8h3.5V20.2z"/>
|
||||
<path d="M9.1,2.8l-4-0.5c-0.2,0-0.4,0-0.6,0.2S4.3,2.7,4.3,2.9l-2,17.5c0,0.4,0.2,0.8,0.7,0.8l4.2,0.5c0.2,0,0.4,0,0.6-0.2C7.9,21.5,8,21.3,8,21.1L9.7,3.6C9.8,3.2,9.5,2.8,9.1,2.8z M6.6,20.2l-2.7-0.3l1.8-16l2.5,0.3L6.6,20.2z"/>
|
||||
<path d="M18.5,9.8c0.4,0,0.8-0.3,0.8-0.8V7.5c0-0.4-0.3-0.8-0.8-0.8s-0.8,0.3-0.8,0.8V9C17.8,9.4,18.1,9.8,18.5,9.8z"/>
|
||||
<path d="M13.5,6.8c-0.4,0-0.8,0.3-0.8,0.8V9c0,0.4,0.3,0.8,0.8,0.8s0.8-0.3,0.8-0.8V7.5C14.2,7.1,13.9,6.8,13.5,6.8z"/>
|
||||
</symbol>
|
||||
<symbol id="approvals" viewBox="0 0 14 14">
|
||||
<path d="M3 1H10C10.5523 1 11 1.44772 11 2V8.5C11 8.77614 11.2239 9 11.5 9C11.7761 9 12 8.77614 12 8.5V2C12 0.895431 11.1046 0 10 0H3C1.89543 0 1 0.89543 1 2V3H0.5C0.223858 3 0 3.22386 0 3.5C0 3.77614 0.223858 4 0.5 4H1V6.5H0.5C0.223858 6.5 0 6.72386 0 7C0 7.27614 0.223858 7.5 0.5 7.5H1V10H0.5C0.223858 10 0 10.2239 0 10.5C0 10.7761 0.223858 11 0.5 11H1V12C1 13.1046 1.89543 14 3 14H6.5C6.77614 14 7 13.7761 7 13.5C7 13.2239 6.77614 13 6.5 13H3C2.44772 13 2 12.5523 2 12V11H2.5C2.77614 11 3 10.7761 3 10.5C3 10.2239 2.77614 10 2.5 10H2V7.5H2.5C2.77614 7.5 3 7.27614 3 7C3 6.72386 2.77614 6.5 2.5 6.5H2V4H2.5C2.77614 4 3 3.77614 3 3.5C3 3.22386 2.77614 3 2.5 3H2V2C2 1.44772 2.44772 1 3 1Z"/>
|
||||
<path d="M8.5 3.00018H4.5C4.22386 3.00018 4 3.22404 4 3.50018C4 3.77633 4.22386 4.00018 4.5 4.00018H8.5C8.77614 4.00018 9 3.77633 9 3.50018C9 3.22404 8.77614 3.00018 8.5 3.00018Z" />
|
||||
<path d="M4.5 5.50018H8.5C8.77614 5.50018 9 5.72404 9 6.00018C9 6.27633 8.77614 6.50018 8.5 6.50018H4.5C4.22386 6.50018 4 6.27633 4 6.00018C4 5.72404 4.22386 5.50018 4.5 5.50018Z" />
|
||||
<path d="M4.5 8.00018H8.5C8.77614 8.00018 9 8.22404 9 8.50018C9 8.77633 8.77614 9.00018 8.5 9.00018H4.5C4.22386 9.00018 4 8.77633 4 8.50018C4 8.22404 4.22386 8.00018 4.5 8.00018Z" />
|
||||
<path d="M10.5 12.6002L9.2 11.3002L8.5 12.0002L10.5 14.0002L14 10.5002L13.3 9.80018L10.5 12.6002Z" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="state-approved" viewBox="0 0 14 14">
|
||||
<path d="M7,0C3.1,0,0,3.1,0,7c0,3.9,3.1,7,7,7c3.9,0,7-3.1,7-7C14,3.1,10.9,0,7,0z M7,12c-2.8,0-5-2.2-5-5s2.2-5,5-5s5,2.2,5,5S9.8,12,7,12z" />
|
||||
</symbol>
|
||||
<symbol id="state-draft" viewBox="0 0 14 14">
|
||||
<path d="M7,0C3.1,0,0,3.1,0,7c0,3.9,3.1,7,7,7c3.9,0,7-3.1,7-7C14,3.1,10.9,0,7,0z M7,12c-2.8,0-5-2.2-5-5s2.2-5,5-5s5,2.2,5,5S9.8,12,7,12z" />
|
||||
<path d="M7,7V3.5c1.9,0,3.5,1.6,3.5,3.5S8.9,10.5,7,10.5V7z" />
|
||||
</symbol>
|
||||
<symbol id="state-effective" viewBox="0 0 14 14">
|
||||
<path d="M7,0C3.1,0,0,3.1,0,7c0,3.9,3.1,7,7,7c3.9,0,7-3.1,7-7C14,3.1,10.9,0,7,0z M9.9,3.9c0.3-0.3,0.8-0.3,1.2,0l0,0c0.3,0.3,0.3,0.9,0,1.2l-5,5c-0.3,0.3-0.9,0.3-1.2,0l-2-2c-0.3-0.3-0.3-0.9,0-1.2c0.2-0.2,0.4-0.2,0.6-0.2s0.4,0.1,0.6,0.2l1.4,1.4l4-4v0L9.9,3.9z" />
|
||||
</symbol>
|
||||
<symbol id="state-rejected" viewBox="0 0 14 14">
|
||||
<path style="fill-rule:evenodd;clip-rule:evenodd;" d="M7,14c3.9,0,7-3.1,7-7c0-3.9-3.1-7-7-7C3.1,0,0,3.1,0,7C0,10.9,3.1,14,7,14z M5,4C4.7,3.7,4.3,3.7,4,4S3.7,4.7,4,5l2,2L4,9C3.7,9.3,3.7,9.7,4,10c0.3,0.3,0.8,0.3,1.1,0l2-2l2,2c0.3,0.3,0.8,0.3,1.1,0c0.3-0.3,0.3-0.8,0-1.1l-2-2l2-2c0.3-0.3,0.3-0.8,0-1.1C9.7,3.7,9.3,3.7,9,4l-2,2L5,4z" />
|
||||
</symbol>
|
||||
<symbol id="state-obsolete" viewBox="0 0 14 14">
|
||||
<path style="fill-rule:evenodd;clip-rule:evenodd;" d="M7,14c3.9,0,7-3.1,7-7c0-3.9-3.1-7-7-7C3.1,0,0,3.1,0,7C0,10.9,3.1,14,7,14z M5,4C4.7,3.7,4.3,3.7,4,4S3.7,4.7,4,5l2,2L4,9C3.7,9.3,3.7,9.7,4,10c0.3,0.3,0.8,0.3,1.1,0l2-2l2,2c0.3,0.3,0.8,0.3,1.1,0c0.3-0.3,0.3-0.8,0-1.1l-2-2l2-2c0.3-0.3,0.3-0.8,0-1.1C9.7,3.7,9.3,3.7,9,4l-2,2L5,4z" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="arrow-up" viewBox="0 0 16 16">
|
||||
<path d="M8 2L3 7L3.705 7.705L7.5 3.915V13.5C7.5 13.7761 7.72386 14 8 14C8.27614 14 8.5 13.7761 8.5 13.5V3.915L12.295 7.705L13 7L8 2Z"/>
|
||||
</symbol>
|
||||
<symbol id="arrow-down" viewBox="0 0 16 16">
|
||||
<path d="M12.295 8.295L8.5 12.085V2.5C8.5 2.22386 8.27614 2 8 2C7.72386 2 7.5 2.22386 7.5 2.5V12.085L3.705 8.295L3 9L8 14L13 9L12.295 8.295Z"/>
|
||||
</symbol>
|
||||
<symbol id="duplicate" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 3C5 1.89543 5.89543 1 7 1H12C13.1046 1 14 1.89543 14 3V10C14 11.1046 13.1046 12 12 12H7C5.89543 12 5 11.1046 5 10V3ZM7 2C6.44772 2 6 2.44772 6 3V10C6 10.5523 6.44772 11 7 11H12C12.5523 11 13 10.5523 13 10V3C13 2.44772 12.5523 2 12 2H7Z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 5C3.44772 5 3 5.44772 3 6V13C3 13.5523 3.44772 14 4 14H9C9.55228 14 10 13.5523 10 13V12H11V13C11 14.1046 10.1046 15 9 15H4C2.89543 15 2 14.1046 2 13V6C2 4.89543 2.89543 4 4 4H5V5H4Z"/>
|
||||
</symbol>
|
||||
<symbol id="edit-description" viewBox="0 0 16 16" width="16" height="16" fill="none">
|
||||
<path d="M2 4C2 2.89543 2.89543 2 4 2H11C12.1046 2 13 2.89543 13 4L12 5V4C12 3.44772 11.5523 3 11 3H4C3.44772 3 3 3.44772 3 4V9C3 9.55228 3.44772 10 4 10H7L6 11H4C2.89543 11 2 10.1046 2 9V4Z" fill="black" fill-opacity="0.6"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.7 7.09988L13.9 6.29988C13.5 5.89988 12.9 5.89988 12.5 6.29988L6 12.7999V14.9999H8.2L14.7 8.49988C15.1 8.09988 15.1 7.49988 14.7 7.09988ZM13.2 6.99988L14 7.79988L12.5 9.29988L11.7 8.49988L13.2 6.99988ZM7 13.9999V13.1999L11 9.19988L11.8 9.99988L7.8 13.9999H7Z" fill="black" fill-opacity="0.6"/>
|
||||
<path d="M4.5529 4.27638C4.67639 4.02939 4.97673 3.92928 5.22372 4.05277L5.67093 4.27638C5.79181 4.33682 5.9021 4.41227 6 4.49982C6.0979 4.41227 6.20819 4.33682 6.32907 4.27638L6.77628 4.05277C7.02327 3.92928 7.32361 4.02939 7.4471 4.27638C7.5706 4.52337 7.47049 4.82371 7.2235 4.9472L6.77628 5.17081C6.61073 5.25358 6.50477 5.42084 6.50005 5.60517L6.50011 5.61802V7.38195L6.50005 7.3948C6.50477 7.57914 6.61073 7.74639 6.77628 7.82917L7.2235 8.05277C7.47049 8.17627 7.5706 8.4766 7.4471 8.72359C7.32361 8.97058 7.02327 9.0707 6.77628 8.9472L6.32907 8.72359C6.20819 8.66315 6.0979 8.5877 6 8.50015C5.9021 8.5877 5.79181 8.66315 5.67093 8.72359L5.22372 8.9472C4.97673 9.0707 4.67639 8.97058 4.5529 8.72359C4.4294 8.4766 4.52951 8.17627 4.7765 8.05277L5.22372 7.82917C5.38927 7.74639 5.49523 7.57914 5.49995 7.3948L5.49989 7.38195V5.61802L5.49995 5.60517C5.49523 5.42084 5.38927 5.25358 5.22372 5.17081L4.7765 4.9472C4.52951 4.82371 4.4294 4.52337 4.5529 4.27638Z" fill="black" fill-opacity="0.6"/>
|
||||
</symbol>
|
||||
<symbol id="edit-guidance" viewBox="0 0 16 16" width="16" height="16" fill="none">
|
||||
<path d="M1 6.5C1 9.07663 2.77181 11.2394 5.16351 11.8365L6.02479 10.9752C3.76289 10.7378 2 8.82478 2 6.5C2 4.01472 4.01472 2 6.5 2C8.82478 2 10.7378 3.76289 10.9752 6.02479L11.8365 5.16351C11.2394 2.77181 9.07663 1 6.5 1C3.46243 1 1 3.46243 1 6.5Z" fill="black" fill-opacity="0.6"/>
|
||||
<path d="M6.08332 3.62628C6.20666 3.54386 6.35166 3.49988 6.5 3.49988C6.69891 3.49988 6.88968 3.5789 7.03033 3.71955C7.17098 3.8602 7.25 4.05097 7.25 4.24988C7.25 4.39821 7.20601 4.54322 7.1236 4.66656C7.04119 4.78989 6.92406 4.88602 6.78701 4.94279C6.64997 4.99955 6.49917 5.01441 6.35368 4.98547C6.2082 4.95653 6.07456 4.8851 5.96967 4.78021C5.86478 4.67532 5.79335 4.54168 5.76441 4.3962C5.73547 4.25071 5.75032 4.09991 5.80709 3.96287C5.86386 3.82582 5.95999 3.70869 6.08332 3.62628Z" fill="black" fill-opacity="0.6"/>
|
||||
<path d="M7 5.99988V8.49988H7.5C7.77614 8.49988 8 8.72373 8 8.99988C8 9.27602 7.77614 9.49988 7.5 9.49988H5.5C5.22386 9.49988 5 9.27602 5 8.99988C5 8.72373 5.22386 8.49988 5.5 8.49988H6V6.49988H5.5C5.22386 6.49988 5 6.27602 5 5.99988C5 5.72374 5.22386 5.49988 5.5 5.49988H6.5C6.77614 5.49988 7 5.72374 7 5.99988Z" fill="black" fill-opacity="0.6"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.7 7.09988L13.9 6.29988C13.5 5.89988 12.9 5.89988 12.5 6.29988L6 12.7999V14.9999H8.2L14.7 8.49988C15.1 8.09988 15.1 7.49988 14.7 7.09988ZM13.2 6.99988L14 7.79988L12.5 9.29988L11.7 8.49988L13.2 6.99988ZM7 13.9999V13.1999L11 9.19988L11.8 9.99988L7.8 13.9999H7Z" fill="black" fill-opacity="0.6"/>
|
||||
</symbol>
|
||||
<symbol id="configure" viewBox="0 0 32 32" fill="currentColor">
|
||||
<path d="M8.99976 1.99976C8.44747 1.99976 7.99976 2.44747 7.99976 2.99976V6.09976C5.69976 6.59976 3.99976 8.59976 3.99976 10.9998C3.99976 13.3998 5.69976 15.3998 7.99976 15.8998L7.99976 28.9998C7.99976 29.552 8.44747 29.9998 8.99976 29.9998C9.55204 29.9998 9.99976 29.552 9.99976 28.9998L9.99976 15.8998C12.2998 15.3998 13.9998 13.3998 13.9998 10.9998C13.9998 8.59976 12.2998 6.59976 9.99976 6.09976V2.99976C9.99976 2.44747 9.55204 1.99976 8.99976 1.99976ZM11.9998 10.9998C11.9998 12.6998 10.6998 13.9998 8.99976 13.9998C7.29976 13.9998 5.99976 12.6998 5.99976 10.9998C5.99976 9.29976 7.29976 7.99976 8.99976 7.99976C10.6998 7.99976 11.9998 9.29976 11.9998 10.9998Z"/>
|
||||
<path d="M22.9998 29.9998C23.552 29.9998 23.9998 29.552 23.9998 28.9998V25.8998C26.2998 25.3998 27.9998 23.3998 27.9998 20.9998C27.9998 18.5998 26.2998 16.5998 23.9998 16.0998V2.99976C23.9998 2.44747 23.552 1.99976 22.9998 1.99976C22.4475 1.99976 21.9998 2.44747 21.9998 2.99976V16.0998C19.6998 16.5998 17.9998 18.5998 17.9998 20.9998C17.9998 23.3998 19.6998 25.3998 21.9998 25.8998V28.9998C21.9998 29.552 22.4475 29.9998 22.9998 29.9998ZM19.9998 20.9998C19.9998 19.2998 21.2998 17.9998 22.9998 17.9998C24.6998 17.9998 25.9998 19.2998 25.9998 20.9998C25.9998 22.6998 24.6998 23.9998 22.9998 23.9998C21.2998 23.9998 19.9998 22.6998 19.9998 20.9998Z"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig",
|
||||
"rigProfile": "assets"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
|
||||
roots: ["./src"],
|
||||
coverageReporters: ["text-summary", "html"]
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
{
|
||||
"string": {
|
||||
"ID": "ID",
|
||||
"Code": "Code",
|
||||
"Number": "Number",
|
||||
"Category": "Category",
|
||||
"CollaborativeDocument": "Collaborative document",
|
||||
"ControlledDocument": "Controlled document",
|
||||
"Requests": "Requests",
|
||||
"EffectiveDate": "Effective date",
|
||||
"PlannedEffectiveDate": "Planned effective date",
|
||||
"Section": "Section",
|
||||
"Rank": "Rang",
|
||||
"CollaborativeSection": "Collaborative section",
|
||||
"CollaboratorSectionId": "Collaborator section ID",
|
||||
"AttachmentsSection": "Attachments section",
|
||||
"DocumentRequest": "Request",
|
||||
"DocumentReviewRequest": "Document review request",
|
||||
"DocumentApprovalRequest": "Document approval request",
|
||||
"ControlledStatus": "Controlled status",
|
||||
"Categories": "Categories",
|
||||
"Guidance": "Guidance",
|
||||
"Required": "Required",
|
||||
"Description": "Description",
|
||||
"SectionTemplate": "Section template",
|
||||
"Major": "Major",
|
||||
"Minor": "Minor",
|
||||
"Patch": "Patch",
|
||||
"DocumentApprovals": "Reviews and Approvals",
|
||||
"ChangeOwner": "Change document owner",
|
||||
"ChangeOwnerHintBeginning": "Transfer ownership of the",
|
||||
"ChangeOwnerHintEnd": "to another person.",
|
||||
"ChangeOwnerWarning": "You will not be able to edit this document after this action.",
|
||||
"SelectOwner": "Select new owner",
|
||||
"CreateDocument": "Create new document",
|
||||
"CreateTemplate": "Create new template",
|
||||
"Documents": "Documents",
|
||||
"Document": "Document",
|
||||
"NewDocumentDialogClose": "Do you want to close this dialog?",
|
||||
"NewDocumentCloseNote": "All changes will be lost",
|
||||
"Cancel": "Cancel",
|
||||
"EditorPlaceholder": "type to start editing...",
|
||||
"Version": "Version",
|
||||
"TemplateVersion": "Template version",
|
||||
"VersionValue": "v{major}.{minor}",
|
||||
"SearchDocument": "Search document...",
|
||||
"CreateEnVersion": "Create version for review",
|
||||
"Approvers": "Approvers",
|
||||
"CoAuthors": "Co-Authors",
|
||||
"Status": "Status",
|
||||
"TemplateName": "Template name",
|
||||
"DocumentApplication": "Controlled Documents",
|
||||
"MyDocuments": "My documents",
|
||||
"Library": "Library",
|
||||
"Labels": "Labels",
|
||||
"Author": "Author",
|
||||
"Review": "Review",
|
||||
"Approval": "Approval",
|
||||
"Reviewer": "Reviewer",
|
||||
"Approver": "Approver",
|
||||
|
||||
"DeleteCategory": "Delete category?",
|
||||
"DeleteCategoryHint": "That action cannot be undone",
|
||||
"DeleteCategoryWarning": "The category cannot be deleted because it is in use",
|
||||
|
||||
"Latest": "Actual",
|
||||
"Draft": "Draft",
|
||||
|
||||
"Reviewers": "Reviewers",
|
||||
|
||||
"ViewMode": "Viewing",
|
||||
"EditMode": "Editing",
|
||||
"ComparisonMode": "Comparing",
|
||||
|
||||
"Compare": "Compare:",
|
||||
"Against": "Against:",
|
||||
|
||||
"ComparisonModeNotSupported": "Comparison mode is not supported.",
|
||||
|
||||
"CreateDraft": "Create Draft",
|
||||
"SendForApproval": "Send for approval",
|
||||
"SendForReview": "Send for review",
|
||||
"CompleteReview": "Complete review",
|
||||
|
||||
"Approve": "Approve",
|
||||
"Reject": "Reject",
|
||||
"ConfirmApproval": "Confirm approval",
|
||||
"ConfirmRejection": "Confirm rejection",
|
||||
"ProvideRejectionReason": "Type rejection reason...",
|
||||
"RejectionReason": "Rejection reason",
|
||||
"ConfirmReviewCompletion": "Confirm review completion",
|
||||
"AddApprovalTitle": "Send your document to approvers",
|
||||
"AddApprovalDescription1": "To prepare your document for approval it should fit few requirements:",
|
||||
"AddApprovalDescription2": "has status 'Draft' or 'Reviewed'",
|
||||
"AddApprovalDescription3": "has no unresolved comments",
|
||||
"AddApprovalDescription4": "if training is planned, the training has status 'Released'",
|
||||
"NoApprovalsDescription": "There are no approvals for this version of document",
|
||||
"CurrentVersion": "Current version",
|
||||
|
||||
"DocumentTemplate": "Template",
|
||||
"DocumentTemplates": "Templates",
|
||||
"DocumentCode": "Document code",
|
||||
"TemplateCode": "Template code",
|
||||
"DocumentCodePlaceholder": "DOC-1",
|
||||
"DocumentPrefixPlaceholder": "DOC",
|
||||
"DocumentPrefix": "Documents prefix",
|
||||
"Sections": "Sections",
|
||||
"TemplateSectionTitle": "Title",
|
||||
"DocumentTemplateCreateLabel": "Template",
|
||||
"DocumentCategoryCreateLabel": "Category",
|
||||
"CreateDocumentCategory": "Create a category",
|
||||
"DocumentCategoryAlreadyExists": "Category \"{title}\" already exists. ",
|
||||
"DocumentCategoryCodeAlreadyExists": "Code \"{code}\" already in use. ",
|
||||
"AttachmentsMax": "Maximum allowed attachments",
|
||||
"Resolve": "Resolve",
|
||||
"Unresolve": "Unresolve",
|
||||
"Resolved": "Resolved",
|
||||
"ShowResolved": "Show resolved comments",
|
||||
"Ordering": "Ordering",
|
||||
"Title": "Title",
|
||||
|
||||
"Effective": "Effective",
|
||||
"Archived": "Archived",
|
||||
"Deleted": "Deleted",
|
||||
"MetaAbstract": "Abstract",
|
||||
|
||||
"ContentTab": "Content",
|
||||
"TeamTab": "Team",
|
||||
"MetaTab": "Metadata",
|
||||
"ChangeControlTab": "Change control",
|
||||
"ReleaseTab": "Release",
|
||||
"HistoryTab": "History",
|
||||
|
||||
"ModificationDate": "Modified at",
|
||||
"Modified": "Modified",
|
||||
"Owner": "Owner",
|
||||
"AssignedTo": "Assigneed to",
|
||||
"Unassigned": "Unassigned",
|
||||
"Untitled": "Untitled",
|
||||
"Copy": "copy",
|
||||
|
||||
"AccessWorkarea": "Access Workarea",
|
||||
"EffectiveLibrary": "Effective library",
|
||||
"WorkingLibrary": "Working library",
|
||||
"CreateDraftQmsDocuments": "Create draft QMS documents",
|
||||
"OwnDocumentAskReviewGetApproval": "Own a QMS document, ask for review, get approval",
|
||||
"ApproveDocuments": "Approve documents",
|
||||
"ReassignOwnershipToAnotherUser": "Reassign ownership to another user",
|
||||
"MakeDocumentEffective": "Make document effective",
|
||||
"CreateDraftQmsTemplates": "Create draft QMS templates",
|
||||
|
||||
"ChangeControl": "Change Control",
|
||||
"ReviewInterval": "Review interval",
|
||||
|
||||
"DocTemplateDeleteSectionTitle": "Confirm deletion of section",
|
||||
"DocTemplateDeleteSectionConfirm": "Are you sure that you want to permanently remove section \"{section}\" from the template?",
|
||||
|
||||
"AddSectionAbove": "Add new section above",
|
||||
"AddSectionBelow": "Add new section below",
|
||||
|
||||
"SelectReviewers": "Select reviewers",
|
||||
"SelectApprovers": "Select approvers",
|
||||
"RequestsToReviewTheDoc": "requests you to review the document",
|
||||
"RequestsToApproveTheDoc": "requests you to approve the document",
|
||||
"Parent": "Parent",
|
||||
"Template": "Template",
|
||||
"GeneralInfo": "General Info",
|
||||
"InProgress": "In progress",
|
||||
|
||||
"EditDescription": "Edit description",
|
||||
"EditGuidance": "Edit guidance",
|
||||
|
||||
"NewDocument": "New document",
|
||||
"NewDocumentCategory": "New category",
|
||||
"NewDocumentTemplate": "New template",
|
||||
|
||||
"LocationStepTitle": "Location",
|
||||
"TemplateStepTitle": "Template",
|
||||
"InfoStepTitle": "Info",
|
||||
"TeamStepTitle": "Team",
|
||||
|
||||
"Duplicate": "Duplicate",
|
||||
|
||||
"TitleAndDescr": "Title and description",
|
||||
"Reason": "Reason",
|
||||
"AbstractPlaceholder": "What is this document about? Who will need it and when? ...",
|
||||
"NewDocCreation": "New document creation",
|
||||
"NewTemplateCreation": "New template creation",
|
||||
"NewTemplatePlaceholder": "What is this template about? Describe how to properly use it...",
|
||||
"CustomReason": "Custom",
|
||||
"ReasonPlaceholder": "Specify the reason...",
|
||||
|
||||
"EditDocument": "Edit document",
|
||||
|
||||
"Key": "Key",
|
||||
"SectionKey": "Section key",
|
||||
"CommentsSequence": "Comments sequence",
|
||||
|
||||
"Email": "Email",
|
||||
"Password": "Password",
|
||||
"FieldIsEmpty": "{field} is empty",
|
||||
"ValidatingCredentials": "Validating credentials...",
|
||||
|
||||
"Organisation": "Organisation",
|
||||
"UnsortedTemplates": "Unsorted templates",
|
||||
"Project": "Project",
|
||||
"Projects": "Projects",
|
||||
"ExternalSpace": "Project space",
|
||||
"DocumentSpaceType": "Documents space type",
|
||||
|
||||
"EffectiveImmediately": "Immediately upon approval",
|
||||
"EffectiveOn": "Make effective on",
|
||||
|
||||
"PeriodicReviewToBeCompleted": "Periodic review to be completed within",
|
||||
"MonthsAfterEffectiveDate": "months after its effective date",
|
||||
"ToBePassedWithin": "To be passed within",
|
||||
"AttemptsAnd": "attempts and",
|
||||
"DaysAfterEffectiveDate": "days after effective date",
|
||||
|
||||
"Index": "Index",
|
||||
"Path": "Path",
|
||||
|
||||
"CreateChildDocument": "Create child document",
|
||||
"CreateChildTemplate": "Create child template",
|
||||
|
||||
"All": "All",
|
||||
|
||||
"Space": "Space",
|
||||
"SelectParent": "Select the parent object for your document",
|
||||
|
||||
"PrefixInUse": "This prefix is already in use by",
|
||||
"CodeInUse": "This code is already in use by",
|
||||
"ChangeCode": "Change Code",
|
||||
"ChangePrefix": "Change Prefix",
|
||||
|
||||
"MarkDocAsDeleted": "Marking document as deleted",
|
||||
"MarkDocsAsDeleted": "Marking multiple documents as deleted",
|
||||
"MarkDocAsDeletedConfirm": "Do you really want to mark the following documents as deleted: {titles}?",
|
||||
|
||||
"DocumentInHierarchy": "Document in hierarchy",
|
||||
"FirstDraftVersion": "This is the first draft version of the document. There's no history yet.",
|
||||
|
||||
"EffectiveDocumentLifecycle": "Effective document lifecycle",
|
||||
|
||||
"ReasonAndImpact": "Reason & Impact",
|
||||
"ImpactAnalysis": "Impact analysis",
|
||||
"ImpactedDocuments": "Impacted documents",
|
||||
|
||||
"CreateDocumentFailed": "Failed to create document",
|
||||
"CreateDocumentTemplateFailed": "Failed to create template",
|
||||
"TryAgain": "Please try again",
|
||||
|
||||
"DescribeChanges": "Describe what was changed...",
|
||||
"DescribeReason": "Describe why it was changed...",
|
||||
"DescribeImpact": "Has an impact on...",
|
||||
"AddDocument": "Add document",
|
||||
"NoDocuments": "No documents",
|
||||
"SysTemplate": "System template",
|
||||
"DocumentTrainingDueDays": "Due days",
|
||||
"DocumentTrainingEnabled": "Enabled",
|
||||
"Own": "Own",
|
||||
"Snapshot": "Snapshot",
|
||||
"Snapshots": "Snapshots",
|
||||
"ControlledSnapshot": "Controlled snapshot",
|
||||
"Name": "Name",
|
||||
"DraftRevision": "Draft revision",
|
||||
"CreateNewDraft": "Draft new version",
|
||||
"ChangeSeverity": "Change severity",
|
||||
"Reference": "Reference",
|
||||
"History": "History",
|
||||
"Signatories": "Signatories",
|
||||
"Page": "Page ",
|
||||
"Of": " of ",
|
||||
"CreatedFromTemplate": "Created from template: ",
|
||||
|
||||
"ViewAll": "View all",
|
||||
"Readonly": "Readonly",
|
||||
|
||||
"NewDocumentSpace": "New documents space",
|
||||
"EditDocumentSpace": "Edit documents space",
|
||||
|
||||
"DocSpaceDescriptionPlaceholder": "Describe your space...",
|
||||
"Members": "Members",
|
||||
"RoleLabel": "Role: {role}",
|
||||
"CreateOrgSpace": "Create organisation space",
|
||||
|
||||
"ReviewDocumentPermission": "Review document",
|
||||
"ReviewDocumentDescription": "Grants users ability to review a document",
|
||||
"ApproveDocumentPermission": "Approve document",
|
||||
"ApproveDocumentDescription": "Grants users ability to approve a document",
|
||||
"CoAuthorDocumentPermission": "Co-author document",
|
||||
"CoAuthorDocumentDescription": "Grants users ability to co-author a document",
|
||||
"CreateDocumentPermission": "Create document",
|
||||
"CreateDocumentDescription": "Grants users ability to create a document",
|
||||
"UpdateDocumentOwnerPermission": "Update document owner",
|
||||
"UpdateDocumentOwnerDescription": "Grants users ability to update a document owner",
|
||||
|
||||
"CreateDocumentCategoryPermission": "Create document category",
|
||||
"CreateDocumentCategoryDescription": "Grants users ability to create a document category",
|
||||
"UpdateDocumentCategoryPermission": "Update document category",
|
||||
"UpdateDocumentCategoryDescription": "Grants users ability to update a document category",
|
||||
"DeleteDocumentCategoryPermission": "Delete document category",
|
||||
"DeleteDocumentCategoryDescription": "Grants users ability to delete a document category",
|
||||
"ConfigLabel": "Controlled Documents",
|
||||
"ConfigDescription": "Extension to manage controlled documents"
|
||||
},
|
||||
"controlledDocStates": {
|
||||
"Empty": "",
|
||||
"Approved": "Approved",
|
||||
"InApproval": "In Approval",
|
||||
"InReview": "In Review",
|
||||
"Reviewed": "Reviewed",
|
||||
"Rejected": "Rejected",
|
||||
"ToReview": "To Review"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
{
|
||||
"string": {
|
||||
"ID": "ID",
|
||||
"Code": "Код",
|
||||
"Number": "Номер",
|
||||
"Category": "Категория",
|
||||
"CollaborativeDocument": "Коллаборативный документ",
|
||||
"ControlledDocument": "Контролируемый документ",
|
||||
"Requests": "Запросы",
|
||||
"EffectiveDate": "Дата выпуска",
|
||||
"PlannedEffectiveDate": "Планируемая дата выпуска",
|
||||
"Section": "Section",
|
||||
"Rank": "Ранг",
|
||||
"CollaborativeSection": "Коллаборативная секция",
|
||||
"CollaboratorSectionId": "ID коллаборационной секции",
|
||||
"AttachmentsSection": "Секция прикрепленных файлов",
|
||||
"DocumentRequest": "Запрос",
|
||||
"DocumentReviewRequest": "Запрос рецензии документа",
|
||||
"DocumentApprovalRequest": "Запрос утверждения документа",
|
||||
"ControlledStatus": "Контролируемый статус",
|
||||
"Categories": "Категории",
|
||||
"Guidance": "Руководство",
|
||||
"Required": "Обязательно",
|
||||
"Description": "Описание",
|
||||
"SectionTemplate": "Шаблон секции",
|
||||
"Major": "Мажорная",
|
||||
"Minor": "Минорная",
|
||||
"Patch": "Патч",
|
||||
"DocumentApprovals": "Рецензии и утверждения",
|
||||
"ChangeOwner": "Изменить владельца документа",
|
||||
"ChangeOwnerHintBeginning": "Передайте права владельца документа",
|
||||
"ChangeOwnerHintEnd": "другому лицу.",
|
||||
"ChangeOwnerWarning": "Вы не сможете редактировать документ после этой операции.",
|
||||
"SelectOwner": "Выберите нового владельца",
|
||||
"CreateDocument": "Создать документ",
|
||||
"CreateTemplate": "Создать шаблон",
|
||||
"Documents": "Документы",
|
||||
"Document": "Документ",
|
||||
"NewDocumentDialogClose": "Вы действительно хотите закрыть окно?",
|
||||
"NewDocumentCloseNote": "Все внесенные изменения будут потеряны",
|
||||
"Cancel": "Отмена",
|
||||
"EditorPlaceholder": "введите для начала...",
|
||||
"Version": "Версия",
|
||||
"TemplateVersion": "Версия шаблона",
|
||||
"VersionValue": "v{major}.{minor}",
|
||||
"SearchDocument": "Найти документ...",
|
||||
"CreateEnVersion": "Создать версию для оценки",
|
||||
"Approvers": "Утверждающие",
|
||||
"CoAuthors": "Соавторы",
|
||||
"Status": "Статус",
|
||||
"TemplateName": "Имя шаблона",
|
||||
"DocumentApplication": "Управляемые Документы",
|
||||
"MyDocuments": "Мои документы",
|
||||
"Library": "Библиотека",
|
||||
"Labels": "Метки",
|
||||
"Author": "Автор",
|
||||
"Review": "Рецензия",
|
||||
"Approval": "Утверждение",
|
||||
"Reviewer": "Рецензент",
|
||||
"Approver": "Утверждающий",
|
||||
|
||||
"DeleteCategory": "Удалить категорию?",
|
||||
"DeleteCategoryHint": "Это действие не может быть отменено",
|
||||
"DeleteCategoryWarning": "Эта категория используется, поэтому не может быть удалена",
|
||||
|
||||
"Latest": "Актуально",
|
||||
"Draft": "Рабочая копия",
|
||||
|
||||
"Reviewers": "Рецензенты",
|
||||
|
||||
"ViewMode": "Просмотр",
|
||||
"EditMode": "Редактирование",
|
||||
"ComparisonMode": "Сравнение",
|
||||
|
||||
"Compare": "Сравнить:",
|
||||
"Against": "с:",
|
||||
|
||||
"ComparisonModeNotSupported": "Сравнение не поддерживается.",
|
||||
|
||||
"CreateDraft": "Создать рабочую копию",
|
||||
"SendForApproval": "Попросить утверждения",
|
||||
"SendForReview": "Попросить рецензию",
|
||||
"CompleteReview": "Закончить рецензию",
|
||||
|
||||
"Approve": "Утвердить",
|
||||
"Reject": "Отказать",
|
||||
"ConfirmApproval": "Подтвердите согласие",
|
||||
"ConfirmRejection": "Подтвердите отказ",
|
||||
"ProvideRejectionReason": "Напишите причину отказа",
|
||||
"RejectionReason": "Причина отказа",
|
||||
"ConfirmReviewCompletion": "Подтвердите окончание рецензии",
|
||||
"AddApprovalTitle": "Отправьте ваш документ на утверждение",
|
||||
"AddApprovalDescription1": "Для отправки вашего документа на утверждение, он должен удовлетворять следующим требованиям:",
|
||||
"AddApprovalDescription2": "иметь статус 'Рабочая копия' или 'Рецензии получены'",
|
||||
"AddApprovalDescription3": "не иметь незавершенных обсуждений",
|
||||
"AddApprovalDescription4": "если запланирован тренинг, то этот тренинг должен иметь статус 'Опубликован'",
|
||||
"NoApprovalsDescription": "Нет утверждений для этой версии документа",
|
||||
"CurrentVersion": "Текущая версия",
|
||||
|
||||
"DocumentTemplate": "Шаблон",
|
||||
"DocumentTemplates": "Шаблоны",
|
||||
"DocumentCode": "Код документа",
|
||||
"TemplateCode": "Код шаблона",
|
||||
"DocumentCodePlaceholder": "ДОК-1",
|
||||
"DocumentPrefixPlaceholder": "ДОК",
|
||||
"DocumentPrefix": "Префикс документов",
|
||||
"Sections": "Разделы",
|
||||
"TemplateSectionTitle": "Заголовок",
|
||||
"DocumentTemplateCreateLabel": "Шалблон",
|
||||
"DocumentCategoryCreateLabel": "Категория",
|
||||
"CreateDocumentCategory": "Создать категорию документа",
|
||||
"DocumentCategoryAlreadyExists": "Категория \"{title}\" уже существует. ",
|
||||
"DocumentCategoryCodeAlreadyExists": "Код \"{code}\" уже используется. ",
|
||||
"AttachmentsMax": "Максимальное число допустимых файлов",
|
||||
"Resolve": "Пометить выполненным",
|
||||
"Unresolve": "Пометить невыполненным",
|
||||
"Resolved": "Выполненный",
|
||||
"ShowResolved": "Показать выполненные",
|
||||
"Ordering": "Сортировка",
|
||||
"Title": "Заголовок",
|
||||
|
||||
"Effective": "Актуальная",
|
||||
"Archived": "Архивированный",
|
||||
"Deleted": "Удаленный",
|
||||
"MetaAbstract": "Описание",
|
||||
|
||||
"ContentTab": "Документ",
|
||||
"TeamTab": "Команда",
|
||||
"MetaTab": "Метаданные",
|
||||
"ChangeControlTab": "Контроль изменений",
|
||||
"ReleaseTab": "Выпуск",
|
||||
"HistoryTab": "История",
|
||||
|
||||
"ModificationDate": "Модифицированно",
|
||||
"Modified": "Модифицированно",
|
||||
"Owner": "Владелец",
|
||||
"AssignedTo": "Назначано на",
|
||||
"Unassigned": "Не назначено",
|
||||
"Untitled": "Без названия",
|
||||
"Copy": "копия",
|
||||
|
||||
"AccessWorkarea": "Доступ к рабочей области",
|
||||
"EffectiveLibrary": "Действующая библиотека",
|
||||
"WorkingLibrary": "Рабочая библиотека",
|
||||
"CreateDraftQmsDocuments": "Создание черновых QMS документов",
|
||||
"OwnDocumentAskReviewGetApproval": "Владение QMS документами, запрос рецензии, получение утверждения",
|
||||
"ApproveDocuments": "Утверждение документов",
|
||||
"ReassignOwnershipToAnotherUser": "Переназначение права владения другому пользователю",
|
||||
"MakeDocumentEffective": "Сделать документ действующим",
|
||||
"CreateDraftQmsTemplates": "Создание черновых QMS шаблонов",
|
||||
|
||||
"ChangeControl": "Контроль Изменений",
|
||||
"ReviewInterval": "Интервал ревью",
|
||||
|
||||
"DocTemplateDeleteSectionTitle": "Подтверждение удаления секции",
|
||||
"DocTemplateDeleteSectionConfirm": "Вы действительно хотите навсегда удалить секцию \"{section}\" из шаблона?",
|
||||
|
||||
"AddSectionAbove": "Добавить новую секцию сверху",
|
||||
"AddSectionBelow": "Добавить новую секцию снизу",
|
||||
|
||||
"SelectReviewers": "Выберите рецензентов",
|
||||
"SelectApprovers": "Выберите утверждающих",
|
||||
"RequestsToReviewTheDoc": "запрашивает у вас рецензию на документ",
|
||||
"RequestsToApproveTheDoc": "запрашивает у вас утверждение документа",
|
||||
"Parent": "Родитель",
|
||||
"Template": "Шаблон",
|
||||
"GeneralInfo": "Общая информация",
|
||||
"InProgress": "В прогресссе",
|
||||
|
||||
"EditDescription": "Редактировать описание",
|
||||
"EditGuidance": "Редактировать руководство",
|
||||
|
||||
"NewDocument": "Новый документ",
|
||||
"NewDocumentCategory": "Новая категория",
|
||||
"NewDocumentTemplate": "Новый шаблон",
|
||||
|
||||
"LocationStepTitle": "Расположение",
|
||||
"TemplateStepTitle": "Шаблон",
|
||||
"InfoStepTitle": "Инфо",
|
||||
"TeamStepTitle": "Команда",
|
||||
|
||||
"Duplicate": "Дублировать",
|
||||
|
||||
"TitleAndDescr": "Заголовок и описание",
|
||||
"Reason": "Причина",
|
||||
"AbstractPlaceholder": "О чем этот документ? Кто будет его использовать и как? ...",
|
||||
"NewDocCreation": "Создание нового документа",
|
||||
"NewTemplateCreation": "Создание нового шаблона",
|
||||
"NewTemplatePlaceholder": "О чем этот шаблон? Опишите как правильно его использовать...",
|
||||
"CustomReason": "Другая",
|
||||
"ReasonPlaceholder": "Укажите причину...",
|
||||
|
||||
"EditDocument": "Редактировать документ",
|
||||
|
||||
"Key": "Ключ",
|
||||
"SectionKey": "Ключ секции",
|
||||
"CommentsSequence": "Последовательность комментариев",
|
||||
|
||||
"Email": "Email",
|
||||
"Password": "Пароль",
|
||||
"FieldIsEmpty": "{field} не задан",
|
||||
"ValidatingCredentials": "Проверка информации...",
|
||||
|
||||
"Organisation": "Организация",
|
||||
"UnsortedTemplates": "Несортированные шаблоны",
|
||||
"Project": "Проект",
|
||||
"Projects": "Проекты",
|
||||
"ExternalSpace": "Пространства проектов",
|
||||
"DocumentSpaceType": "Тип пространства документов",
|
||||
|
||||
"EffectiveImmediately": "Сразу после утверждения",
|
||||
"EffectiveOn": "Сделать эффективным:",
|
||||
|
||||
"PeriodicReviewToBeCompleted": "Периодическое ревью документа в течение",
|
||||
"MonthsAfterEffectiveDate": "месяцев после даты, когда документ стал эффективным",
|
||||
"ToBePassedWithin": "Пройти в течение",
|
||||
"AttemptsAnd": "попыток и",
|
||||
"DaysAfterEffectiveDate": "дней после даты, когда документ стал эффективным",
|
||||
|
||||
"Index": "Индекс",
|
||||
"Path": "Путь",
|
||||
|
||||
"CreateChildDocument": "Создать дочерний документ",
|
||||
"CreateChildTemplate": "Создать дочерний шаблон",
|
||||
|
||||
"All": "Все",
|
||||
|
||||
"Space": "Пространство",
|
||||
"SelectParent": "Выберите родительский объект для Вашего документа",
|
||||
|
||||
"PrefixInUse": "Этот префикс уже используется",
|
||||
"CodeInUse": "Этот код уже используется",
|
||||
"ChangeCode": "Изменить код",
|
||||
"ChangePrefix": "Изменить префикс",
|
||||
|
||||
"MarkDocAsDeleted": "Пометить документ как удаленный",
|
||||
"MarkDocsAsDeleted": "Пометить несколько документов как удаленные",
|
||||
"MarkDocAsDeletedConfirm": "Вы действительно хотите пометить следующие документы как удаленные: {titles}?",
|
||||
|
||||
"DocumentInHierarchy": "Документ в иерархии",
|
||||
"FirstDraftVersion": "Это первая рабочая копия документа. Для него пока нет истории.",
|
||||
|
||||
"EffectiveDocumentLifecycle": "Жизненный цикл эффективного документа",
|
||||
|
||||
"ReasonAndImpact": "Причина & Воздействие",
|
||||
"ImpactAnalysis": "Анализ воздействия",
|
||||
"ImpactedDocuments": "Подверженные документы",
|
||||
|
||||
"CreateDocumentFailed": "Ошибка при создании документа",
|
||||
"CreateDocumentTemplateFailed": "Ошибка при создании шаблона",
|
||||
"TryAgain": "Пожалуйста, попробуйте снова",
|
||||
|
||||
"DescribeChanges": "Опишите, что поменялось...",
|
||||
"DescribeReason": "Опишите, почему это поменялось...",
|
||||
"DescribeImpact": "Воздействует на...",
|
||||
"AddDocument": "Добавить документ",
|
||||
"NoDocuments": "Нет документов",
|
||||
"SysTemplate": "Системный шаблон",
|
||||
|
||||
"DocumentTrainingDueDays": "Срок",
|
||||
"DocumentTrainingEnabled": "Включен",
|
||||
"Own": "Собственный",
|
||||
"Snapshot": "Мгновенный снимок",
|
||||
"Snapshots": "Мгновенные снимки",
|
||||
"ControlledSnapshot": "Контролируемый мгновенный снимок",
|
||||
"Name": "Название",
|
||||
"DraftRevision": "Ревизия рабочей копии",
|
||||
"CreateNewDraft": "Создать новую рабочую копию",
|
||||
"ChangeSeverity": "Серьезность изменений",
|
||||
"Reference": "Референс",
|
||||
"History": "История",
|
||||
"Signatories": "Подписавшие",
|
||||
"Page": "Страница ",
|
||||
"Of": " из ",
|
||||
"CreatedFromTemplate": "Создан из шаблона: ",
|
||||
|
||||
"ViewAll": "Показать все",
|
||||
"Readonly": "Только для чтения",
|
||||
|
||||
"NewDocumentSpace": "Новое пространство документов",
|
||||
"EditDocumentSpace": "Редактировать пространство документов",
|
||||
|
||||
"DocSpaceDescriptionPlaceholder": "Опишите Ваше пространство...",
|
||||
"Members": "Участники",
|
||||
"RoleLabel": "Роль: {role}",
|
||||
"CreateOrgSpace": "Создать пространство организации",
|
||||
|
||||
"ReviewDocumentPermission": "Рецензировать документ",
|
||||
"ReviewDocumentDescription": "Предоставляет пользователям разрешение рецензировать документ",
|
||||
"ApproveDocumentPermission": "Утверждать документ",
|
||||
"ApproveDocumentDescription": "Предоставляет пользователям разрешение утверждать документ",
|
||||
"CoAuthorDocumentPermission": "Быть со-автором документа",
|
||||
"CoAuthorDocumentDescription": "Предоставляет пользователям разрешение быть со-автором документа",
|
||||
"CreateDocumentPermission": "Создавать документ",
|
||||
"CreateDocumentDescription": "Предоставляет пользователям разрешение создавать документ",
|
||||
"UpdateDocumentOwnerPermission": "Изменять владельца документа",
|
||||
"UpdateDocumentOwnerDescription": "Предоставляет пользователям разрешение изменять владельца документа",
|
||||
|
||||
"CreateDocumentCategoryPermission": "Создавать категорию",
|
||||
"CreateDocumentCategoryDescription": "Предоставляет пользователям разрешение создавать категорию",
|
||||
"UpdateDocumentCategoryPermission": "Обновлять категорию",
|
||||
"UpdateDocumentCategoryDescription": "Предоставляет пользователям разрешение обновлять категорию",
|
||||
"DeleteDocumentCategoryPermission": "Удалять категорию",
|
||||
"DeleteDocumentCategoryDescription": "Предоставляет пользователям разрешение удалять категорию",
|
||||
"ConfigLabel": "Управляемые Документы",
|
||||
"ConfigDescription": "Расширение для управления управялемыми документами"
|
||||
},
|
||||
"controlledDocStates": {
|
||||
"Empty": "",
|
||||
"Approved": "Утвержден",
|
||||
"InApproval": "В процессе утверждения",
|
||||
"InReview": "Рецензируется",
|
||||
"Reviewed": "Рецензии получены",
|
||||
"Rejected": "Отказано",
|
||||
"ToReview": "Для рецензии"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@hcengineering/controlled-documents-assets",
|
||||
"version": "0.1.0",
|
||||
"main": "src/index.ts",
|
||||
"template": "@hcengineering/assets-package",
|
||||
"scripts": {
|
||||
"build": "compile",
|
||||
"test": "jest --passWithNoTests --silent",
|
||||
"build:docs": "",
|
||||
"format": "format src",
|
||||
"build:watch": "compile",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:test": "jest --passWithNoTests --silent",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"prettier": "^3.1.0",
|
||||
"@types/node": "~20.11.16",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"@types/jest": "^29.5.5",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/platform": "^0.6.9",
|
||||
"@hcengineering/controlled-documents": "^0.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { makeLocalesTest } from '@hcengineering/platform'
|
||||
|
||||
it(
|
||||
'Locales are equale',
|
||||
makeLocalesTest((lang) => import(`../../lang/${lang}.json`))
|
||||
)
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Copyright © 2024 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.
|
||||
//
|
||||
import documents from '@hcengineering/controlled-documents'
|
||||
import { loadMetadata } from '@hcengineering/platform'
|
||||
|
||||
const icons = require('../assets/icons.svg') as string // eslint-disable-line
|
||||
loadMetadata(documents.icon, {
|
||||
CheckmarkCircle: `${icons}#checkmark-circle`,
|
||||
Approvals: `${icons}#approvals`,
|
||||
DocumentApplication: `${icons}#documentapplication`,
|
||||
NewDocument: `${icons}#newdocument`,
|
||||
Document: `${icons}#document`,
|
||||
Library: `${icons}#library`,
|
||||
StateApproved: `${icons}#state-approved`,
|
||||
StateDraft: `${icons}#state-draft`,
|
||||
StateEffective: `${icons}#state-effective`,
|
||||
StateRejected: `${icons}#state-rejected`,
|
||||
StateObsolete: `${icons}#state-obsolete`,
|
||||
ArrowUp: `${icons}#arrow-up`,
|
||||
ArrowDown: `${icons}#arrow-down`,
|
||||
Duplicate: `${icons}#duplicate`,
|
||||
EditDescription: `${icons}#edit-description`,
|
||||
EditGuidance: `${icons}#edit-guidance`,
|
||||
Configure: `${icons}#configure`
|
||||
})
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/assets/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"types": ["node", "jest"],
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/ui/eslint.config.json'],
|
||||
parserOptions: { tsconfigRootDir: __dirname }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"trailingComma": "none",
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 120,
|
||||
"useTabs": false,
|
||||
"bracketSpacing": true,
|
||||
"proseWrap": "preserve",
|
||||
"plugins": [
|
||||
"prettier-plugin-svelte"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.svelte",
|
||||
"options": {
|
||||
"parser": "svelte"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig",
|
||||
"rigProfile": "ui"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
|
||||
roots: ["./src"],
|
||||
coverageReporters: ["text-summary", "html"]
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"name": "@hcengineering/controlled-documents-resources",
|
||||
"version": "0.1.0",
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
"build": "compile ui",
|
||||
"build:docs": "api-extractor run --local",
|
||||
"format": "format src",
|
||||
"svelte-check": "do-svelte-check",
|
||||
"_phase:svelte-check": "do-svelte-check",
|
||||
"build:watch": "compile ui",
|
||||
"_phase:build": "compile ui",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"svelte-loader": "^3.2.0",
|
||||
"sass": "^1.53.0",
|
||||
"svelte-preprocess": "^5.1.3",
|
||||
"@hcengineering/platform-rig": "^0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"prettier-plugin-svelte": "^3.2.2",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"prettier": "^3.1.0",
|
||||
"svelte-check": "^3.6.9",
|
||||
"typescript": "^5.3.3",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"@types/jest": "^29.5.5",
|
||||
"eslint-plugin-svelte": "^2.35.1",
|
||||
"svelte-eslint-parser": "^0.33.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/platform": "^0.6.9",
|
||||
"@hcengineering/login": "^0.6.8",
|
||||
"@hcengineering/ui": "^0.6.11",
|
||||
"@hcengineering/setting": "^0.6.11",
|
||||
"@hcengineering/presentation": "^0.6.2",
|
||||
"@hcengineering/core": "^0.6.28",
|
||||
"@hcengineering/view": "^0.6.9",
|
||||
"@hcengineering/attachment-resources": "^0.6.0",
|
||||
"@hcengineering/notification-resources": "^0.6.0",
|
||||
"@hcengineering/panel": "^0.6.15",
|
||||
"@hcengineering/view-resources": "^0.6.0",
|
||||
"@hcengineering/attachment": "^0.6.9",
|
||||
"@hcengineering/notification": "^0.6.16",
|
||||
"@hcengineering/contact": "^0.6.20",
|
||||
"@hcengineering/contact-resources": "^0.6.0",
|
||||
"@hcengineering/tags": "^0.6.12",
|
||||
"@hcengineering/text-editor": "^0.6.0",
|
||||
"@hcengineering/activity": "^0.6.0",
|
||||
"@hcengineering/request": "^0.6.6",
|
||||
"@hcengineering/request-resources": "^0.6.0",
|
||||
"@hcengineering/chunter": "^0.6.12",
|
||||
"@hcengineering/chunter-resources": "^0.6.0",
|
||||
"@hcengineering/workbench": "^0.6.9",
|
||||
"@hcengineering/theme": "^0.6.3",
|
||||
"@hcengineering/controlled-documents": "^0.1.0",
|
||||
"@hcengineering/training": "^0.1.0",
|
||||
"@hcengineering/training-resources": "^0.1.0",
|
||||
"@tiptap/core": "^2.2.4",
|
||||
"effector": "~22.8.7",
|
||||
"svelte": "^4.2.12",
|
||||
"yjs": "^13.5.52",
|
||||
"slugify": "^1.6.6",
|
||||
"fast-equals": "^5.0.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('autoprefixer')
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<!--
|
||||
// Copyright © 2024 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 documents, { DocumentCategory } from '@hcengineering/controlled-documents'
|
||||
import { Class, DocumentQuery, Ref, TypedSpace } from '@hcengineering/core'
|
||||
import { ActionContext } from '@hcengineering/presentation'
|
||||
import { Button, IconAdd, Loading, showPopup } from '@hcengineering/ui'
|
||||
import view, { Viewlet, ViewletPreference, ViewOptions } from '@hcengineering/view'
|
||||
import { TableBrowser, ViewletPanelHeader, checkMyPermission, permissionsStore } from '@hcengineering/view-resources'
|
||||
|
||||
import document from '../plugin'
|
||||
import CreateDocumentCategory from './CreateDocumentCategory.svelte'
|
||||
|
||||
export let query: DocumentQuery<DocumentCategory> = {}
|
||||
export let space: Ref<TypedSpace> = documents.space.QualityDocuments
|
||||
|
||||
let resultQuery: DocumentQuery<DocumentCategory> = { ...query }
|
||||
|
||||
let viewlet: Viewlet | undefined
|
||||
let viewOptions: ViewOptions | undefined
|
||||
let preference: ViewletPreference | undefined = undefined
|
||||
let loading = true
|
||||
|
||||
$: canCreate = checkMyPermission(documents.permission.CreateDocumentCategory, space, $permissionsStore)
|
||||
|
||||
const _class: Ref<Class<DocumentCategory>> = document.class.DocumentCategory
|
||||
|
||||
function showCreateDialog (): void {
|
||||
showPopup(CreateDocumentCategory, {}, 'top')
|
||||
}
|
||||
</script>
|
||||
|
||||
<ActionContext
|
||||
context={{
|
||||
mode: 'browser'
|
||||
}}
|
||||
/>
|
||||
<div class="antiPanel-component">
|
||||
<ViewletPanelHeader
|
||||
viewletQuery={{
|
||||
attachTo: _class,
|
||||
descriptor: view.viewlet.Table
|
||||
}}
|
||||
bind:viewlet
|
||||
bind:loading
|
||||
bind:viewOptions
|
||||
bind:preference
|
||||
{_class}
|
||||
title={document.string.Categories}
|
||||
{query}
|
||||
bind:resultQuery
|
||||
>
|
||||
<div slot="header-tools">
|
||||
{#if canCreate}
|
||||
<Button
|
||||
icon={IconAdd}
|
||||
label={document.string.DocumentCategoryCreateLabel}
|
||||
size="small"
|
||||
kind="primary"
|
||||
on:click={showCreateDialog}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</ViewletPanelHeader>
|
||||
|
||||
{#if loading}
|
||||
<Loading />
|
||||
{:else if viewlet && viewOptions}
|
||||
<TableBrowser
|
||||
{_class}
|
||||
config={preference?.config ?? viewlet.config}
|
||||
options={viewlet.options}
|
||||
query={resultQuery}
|
||||
showNotification
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -0,0 +1,176 @@
|
||||
<!--
|
||||
//
|
||||
// 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 { createEventDispatcher } from 'svelte'
|
||||
import { Employee, PersonAccount } from '@hcengineering/contact'
|
||||
import {
|
||||
AttachedData,
|
||||
Class,
|
||||
generateId,
|
||||
getCollaborativeDoc,
|
||||
getCurrentAccount,
|
||||
Mixin,
|
||||
Ref,
|
||||
SortingOrder
|
||||
} from '@hcengineering/core'
|
||||
import { Card, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { createFocusManager, EditBox, FocusHandler } from '@hcengineering/ui'
|
||||
import { ObjectBox } from '@hcengineering/view-resources'
|
||||
import {
|
||||
type ControlledDocument,
|
||||
type DocumentTemplate,
|
||||
type DocumentCategory,
|
||||
type ChangeControl,
|
||||
type DocumentSpace,
|
||||
DocumentState,
|
||||
createControlledDocFromTemplate
|
||||
} from '@hcengineering/controlled-documents'
|
||||
|
||||
import documents from '../plugin'
|
||||
|
||||
export let documentClass: Ref<Class<ControlledDocument>> = documents.class.ControlledDocument
|
||||
export let templateMixin: Ref<Mixin<DocumentTemplate>> = documents.mixin.DocumentTemplate
|
||||
export let initTemplateId: Ref<DocumentTemplate> | undefined = undefined
|
||||
export let excludedTemplates: Array<Ref<DocumentTemplate>> | undefined = undefined
|
||||
export let isTemplateMandatory: boolean = false
|
||||
export let isTemplateReadonly: boolean = false
|
||||
export let space: Ref<DocumentSpace>
|
||||
|
||||
export function canClose (): boolean {
|
||||
return object.title === ''
|
||||
}
|
||||
|
||||
const id = generateId<ControlledDocument>()
|
||||
const currentUser = getCurrentAccount() as PersonAccount
|
||||
|
||||
const object: AttachedData<ControlledDocument> = {
|
||||
template: '' as Ref<DocumentTemplate>,
|
||||
title: '',
|
||||
code: '',
|
||||
prefix: '',
|
||||
labels: 0,
|
||||
major: 0,
|
||||
minor: 1,
|
||||
commentSequence: 0,
|
||||
author: currentUser.person as Ref<Employee>,
|
||||
owner: currentUser.person as Ref<Employee>,
|
||||
seqNumber: 0,
|
||||
category: '' as Ref<DocumentCategory>,
|
||||
abstract: '',
|
||||
state: DocumentState.Draft,
|
||||
sections: 0,
|
||||
requests: 0,
|
||||
snapshots: 0,
|
||||
reviewers: [],
|
||||
approvers: [],
|
||||
coAuthors: [],
|
||||
changeControl: '' as Ref<ChangeControl>,
|
||||
content: getCollaborativeDoc(generateId())
|
||||
}
|
||||
|
||||
let templateId: Ref<DocumentTemplate> | undefined = initTemplateId
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
async function handleOkAction (): Promise<void> {
|
||||
if (isTemplateMandatory && !templateId) {
|
||||
return
|
||||
}
|
||||
|
||||
await createControlledDocFromTemplate(client, templateId, id, object, space, undefined, undefined, documentClass)
|
||||
|
||||
dispatch('close', id)
|
||||
}
|
||||
|
||||
const manager = createFocusManager()
|
||||
|
||||
$: if (templateId === undefined) {
|
||||
client
|
||||
.findOne(
|
||||
templateMixin,
|
||||
{ _class: documentClass, _id: { $nin: excludedTemplates ?? [] } },
|
||||
{ sort: { modifiedOn: SortingOrder.Descending } }
|
||||
)
|
||||
.then((tpl) => {
|
||||
if (tpl !== undefined) {
|
||||
templateId = tpl._id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const templateQuery = createQuery()
|
||||
let template: DocumentTemplate | undefined
|
||||
|
||||
$: templateQuery.query(
|
||||
templateMixin,
|
||||
{ _class: documentClass, _id: templateId },
|
||||
(res) => {
|
||||
if (res) {
|
||||
;[template] = res
|
||||
object.template = template._id
|
||||
object.prefix = template.prefix
|
||||
object.category = template.category
|
||||
}
|
||||
},
|
||||
{
|
||||
limit: 1
|
||||
}
|
||||
)
|
||||
|
||||
// Note: previously there was inline document section editing. Restore later if will be needed as per UX.
|
||||
// See QE CreateReport component for reference. May unify general approach on creating docs with content right away.
|
||||
</script>
|
||||
|
||||
<FocusHandler {manager} />
|
||||
|
||||
<Card
|
||||
label={documents.string.CreateDocument}
|
||||
okAction={handleOkAction}
|
||||
canSave={object.title.length > 0 && (templateId !== undefined || !isTemplateMandatory)}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<ObjectBox
|
||||
_class={templateMixin}
|
||||
excluded={excludedTemplates}
|
||||
readonly={isTemplateReadonly}
|
||||
docQuery={{ _class: documentClass }}
|
||||
options={{ sort: { modifiedOn: SortingOrder.Descending } }}
|
||||
bind:value={templateId}
|
||||
kind={isTemplateReadonly ? 'ghost' : 'no-border'}
|
||||
size="small"
|
||||
label={documents.string.DocumentTemplate}
|
||||
icon={documents.icon.Document}
|
||||
searchField="title"
|
||||
allowDeselect={false}
|
||||
showNavigate={false}
|
||||
docProps={{ disableLink: true, showTitle: true }}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<div class="fs-title flex-row-center clear-mins">
|
||||
<EditBox
|
||||
placeholder={documents.string.Title}
|
||||
bind:value={object.title}
|
||||
kind="large-style"
|
||||
autoFocus
|
||||
focusIndex={1}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -0,0 +1,216 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2023 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 { createEventDispatcher } from 'svelte'
|
||||
import { Attachment } from '@hcengineering/attachment'
|
||||
import { AttachmentPresenter, AttachmentStyledBox } from '@hcengineering/attachment-resources'
|
||||
import { generateId, Ref, Space } from '@hcengineering/core'
|
||||
import { Card, getClient } from '@hcengineering/presentation'
|
||||
import { Button, EditBox, IconAttachment, tooltip } from '@hcengineering/ui'
|
||||
import { DocumentCategory } from '@hcengineering/controlled-documents'
|
||||
|
||||
import IconWarning from './icons/IconWarning.svelte'
|
||||
import documents from '../plugin'
|
||||
|
||||
export let space: Ref<Space> = documents.space.QualityDocuments
|
||||
|
||||
const _id = generateId<DocumentCategory>()
|
||||
let code: string = ''
|
||||
let _title: string = ''
|
||||
$: title = _title.trim()
|
||||
let description: string = ''
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
let descriptionBox: AttachmentStyledBox
|
||||
|
||||
async function handleOkAction (): Promise<void> {
|
||||
if (isCodeWrong || isTitleWrong) {
|
||||
return
|
||||
}
|
||||
await client.createDoc(
|
||||
documents.class.DocumentCategory,
|
||||
space,
|
||||
{
|
||||
title,
|
||||
code,
|
||||
description,
|
||||
attachments: 0
|
||||
},
|
||||
_id
|
||||
)
|
||||
await descriptionBox.createAttachments(_id)
|
||||
|
||||
dispatch('close', _id)
|
||||
}
|
||||
|
||||
let existingCategories: string[] = []
|
||||
let existingCodes: string[] = []
|
||||
$: void client.findAll(documents.class.DocumentCategory, {}).then((cats) => {
|
||||
existingCategories = cats.map((cat) => cat.title)
|
||||
existingCodes = cats.map((cat) => cat.code)
|
||||
})
|
||||
|
||||
let isCodeWrong: boolean = false
|
||||
let isCodeInUse: boolean = false
|
||||
$: isCodeInUse = existingCodes.includes(code)
|
||||
$: isCodeWrong = code === '' || isCodeInUse
|
||||
|
||||
let isTitleWrong: boolean = false
|
||||
let isTitleInUse: boolean = false
|
||||
$: isTitleInUse = existingCategories.includes(title)
|
||||
$: isTitleWrong = title === '' || isTitleInUse
|
||||
|
||||
let attachments: Map<Ref<Attachment>, Attachment> = new Map<Ref<Attachment>, Attachment>()
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={documents.string.CreateDocumentCategory}
|
||||
okAction={handleOkAction}
|
||||
canSave={!isCodeWrong && !isTitleWrong}
|
||||
hideAttachments={attachments.size === 0}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<div class="flex-row-center flex-between title-editbox">
|
||||
<EditBox
|
||||
placeholder={documents.string.Title}
|
||||
bind:value={_title}
|
||||
kind={'large-style'}
|
||||
required
|
||||
focusIndex={1}
|
||||
maxWidth={'38rem'}
|
||||
autoFocus
|
||||
on:input={() => {
|
||||
code = _title
|
||||
.split(' ')
|
||||
.map((it) => it[0]?.toUpperCase())
|
||||
.filter((it) => it)
|
||||
.join('')
|
||||
}}
|
||||
/>
|
||||
<div class="icon-placeholder">
|
||||
{#if isTitleInUse}
|
||||
<div
|
||||
use:tooltip={{ label: documents.string.DocumentCategoryAlreadyExists, props: { title }, direction: 'left' }}
|
||||
>
|
||||
<IconWarning size="small" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-row-center flex-between code-editbox">
|
||||
<EditBox
|
||||
placeholder={documents.string.Code}
|
||||
bind:value={code}
|
||||
kind={'large-style'}
|
||||
required
|
||||
focusIndex={2}
|
||||
maxWidth={'38rem'}
|
||||
on:input={() => {
|
||||
code = code.trim()
|
||||
}}
|
||||
/>
|
||||
<div class="icon-placeholder">
|
||||
{#if isCodeInUse}
|
||||
<div
|
||||
use:tooltip={{
|
||||
label: documents.string.DocumentCategoryCodeAlreadyExists,
|
||||
props: { code },
|
||||
direction: 'left'
|
||||
}}
|
||||
>
|
||||
<IconWarning size="small" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="description-box">
|
||||
<AttachmentStyledBox
|
||||
bind:this={descriptionBox}
|
||||
bind:content={description}
|
||||
placeholder={documents.string.Description}
|
||||
objectId={_id}
|
||||
_class={documents.class.DocumentCategory}
|
||||
{space}
|
||||
focusIndex={3}
|
||||
alwaysEdit
|
||||
showButtons={false}
|
||||
kind={'normal'}
|
||||
isScrollable={false}
|
||||
enableAttachments={false}
|
||||
on:attachments={(ev) => {
|
||||
if (ev.detail.size > 0) attachments = ev.detail.values
|
||||
else if (ev.detail.size === 0 && ev.detail.values === true) {
|
||||
attachments.clear()
|
||||
attachments = attachments
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<svelte:fragment slot="attachments">
|
||||
{#if attachments.size > 0}
|
||||
{#each Array.from(attachments.values()) as attachment}
|
||||
<AttachmentPresenter
|
||||
value={attachment}
|
||||
showPreview
|
||||
removable
|
||||
on:remove={(result) => {
|
||||
if (result.detail !== undefined) descriptionBox.removeAttachmentById(result.detail._id)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button
|
||||
focusIndex={10}
|
||||
icon={IconAttachment}
|
||||
iconProps={{ fill: 'var(--theme-dark-color)' }}
|
||||
size={'large'}
|
||||
kind={'ghost'}
|
||||
on:click={() => {
|
||||
descriptionBox.handleAttach()
|
||||
}}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
|
||||
<style lang="scss">
|
||||
.category-warning-message {
|
||||
color: var(--highlight-red);
|
||||
}
|
||||
|
||||
.title-editbox {
|
||||
padding: 0rem 0.5rem 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
.code-editbox {
|
||||
padding: 0.5rem 0.5rem 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
.description-box {
|
||||
padding: 0.5rem 0.5rem 0rem 0.75rem;
|
||||
}
|
||||
|
||||
.icon-placeholder {
|
||||
width: 1rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,140 @@
|
||||
<!--
|
||||
// Copyright © 2024 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 { createEventDispatcher } from 'svelte'
|
||||
import type { Class, DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import { Label, showPopup, ActionIcon, IconClose, IconAdd, Icon } from '@hcengineering/ui'
|
||||
import type { IconSize } from '@hcengineering/ui'
|
||||
import { ObjectPresenter } from '@hcengineering/view-resources'
|
||||
import documents, { type Document } from '@hcengineering/controlled-documents'
|
||||
|
||||
import documentsRes from '../plugin'
|
||||
import DocumentsPopup from './DocumentsPopup.svelte'
|
||||
|
||||
export let items: Ref<Document>[] = []
|
||||
export let readonlyItems = new Set<Ref<Document>>()
|
||||
export let _class: Ref<Class<Document>> = documents.class.Document
|
||||
export let docQuery: DocumentQuery<Document> | undefined = undefined
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let actionLabel: IntlString = documentsRes.string.AddDocument
|
||||
export let size: IconSize = 'x-small'
|
||||
export let width: string | undefined = undefined
|
||||
export let readonly: boolean = false
|
||||
|
||||
$: fixedItems = readonly ? items : items.filter((item) => readonlyItems.has(item))
|
||||
$: editableItems = readonly ? [] : items.filter((item) => !readonlyItems.has(item))
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function addPerson (evt: Event): Promise<void> {
|
||||
showPopup(
|
||||
DocumentsPopup,
|
||||
{
|
||||
_class,
|
||||
label,
|
||||
docQuery,
|
||||
multiSelect: true,
|
||||
allowDeselect: false,
|
||||
selectedDocuments: items,
|
||||
ignoreDocuments: Array.of(readonlyItems),
|
||||
readonly
|
||||
},
|
||||
evt.target as HTMLElement,
|
||||
undefined,
|
||||
(result) => {
|
||||
if (result != null) {
|
||||
items = result
|
||||
dispatch('update', items)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const removeDocument = (removed: Ref<Document>): void => {
|
||||
const newItems = items.filter((it) => it !== removed)
|
||||
|
||||
dispatch('update', newItems)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-col" style:width={width ?? 'auto'}>
|
||||
<div class="flex-row-center flex-wrap">
|
||||
{#each fixedItems as doc}
|
||||
<div class="doctag-container gap-1-5">
|
||||
<ObjectPresenter
|
||||
objectId={doc}
|
||||
_class={documents.class.Document}
|
||||
props={{ withIcon: true, withTitle: true, isRegular: true }}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
{#each editableItems as doc}
|
||||
<div class="doctag-container gap-1-5">
|
||||
<ObjectPresenter
|
||||
objectId={doc}
|
||||
_class={documents.class.Document}
|
||||
props={{ withIcon: true, withTitle: true, isRegular: true }}
|
||||
/>
|
||||
<ActionIcon
|
||||
icon={IconClose}
|
||||
size={size === 'inline' ? 'x-small' : 'small'}
|
||||
action={() => {
|
||||
removeDocument(doc)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{#if !readonly}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="addButton {size === 'inline' ? 'small' : 'medium'} overflow-label gap-2 cursor-pointer"
|
||||
class:mt-2={items.length > 0}
|
||||
on:click={addPerson}
|
||||
>
|
||||
<span><Label label={actionLabel} /></span>
|
||||
<Icon icon={IconAdd} size={size === 'inline' ? 'x-small' : 'small'} fill={'var(--theme-dark-color)'} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.doctag-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 0.5rem 0.5rem 0;
|
||||
padding: 0.375rem 0.625rem 0.375rem 0.5rem;
|
||||
background-color: var(--theme-button-default);
|
||||
border: 1px solid var(--theme-button-border);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
.addButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 500;
|
||||
color: var(--theme-dark-color);
|
||||
|
||||
&.small {
|
||||
height: 0.875rem;
|
||||
font-size: 0.75rem;
|
||||
line-height: 0.75rem;
|
||||
}
|
||||
&.medium {
|
||||
height: 1.125rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { DocumentMeta } from '@hcengineering/controlled-documents'
|
||||
|
||||
import DocumentIcon from './icons/DocumentIcon.svelte'
|
||||
|
||||
export let value: WithLookup<DocumentMeta>
|
||||
</script>
|
||||
|
||||
<div class="flex-row-center">
|
||||
<div class="flex-center p-1 content-dark-color flex-no-shrink mr-2-5">
|
||||
<DocumentIcon size={'medium'} />
|
||||
</div>
|
||||
<span class="overflow-label">
|
||||
{value.title}
|
||||
</span>
|
||||
</div>
|
||||
@@ -0,0 +1,59 @@
|
||||
<!--
|
||||
// Copyright © 2024 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 { SortingOrder } from '@hcengineering/core'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Icon, tooltip } from '@hcengineering/ui'
|
||||
import documents, { Document, DocumentMeta, getDocumentName } from '@hcengineering/controlled-documents'
|
||||
|
||||
import document from '../plugin'
|
||||
|
||||
export let value: DocumentMeta
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let lastDoc: Document | undefined
|
||||
client
|
||||
.findOne(
|
||||
documents.class.Document,
|
||||
{
|
||||
attachedTo: value._id
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
createdOn: SortingOrder.Descending
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(
|
||||
(res) => {
|
||||
lastDoc = res
|
||||
},
|
||||
(err) => {
|
||||
console.warn(`Cannot find Document for meta: ${value._id}. Error: ${err}`)
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
{#if lastDoc}
|
||||
{@const title = getDocumentName(lastDoc)}
|
||||
<div class="flex-row-center" use:tooltip={{ label: getEmbeddedLabel(title) }}>
|
||||
<div class="icon mr-1">
|
||||
<Icon icon={document.icon.Document} size={'small'} />
|
||||
</div>
|
||||
<span class="label">{title}</span>
|
||||
</div>
|
||||
{/if}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user