mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-21 16:09:12 +03:00
Inventory (#823)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
parent
cd6148436f
commit
09d3cd4238
@ -36,6 +36,9 @@ specifiers:
|
||||
'@rush-temp/gmail-resources': file:./projects/gmail-resources.tgz
|
||||
'@rush-temp/image-cropper': file:./projects/image-cropper.tgz
|
||||
'@rush-temp/image-cropper-resources': file:./projects/image-cropper-resources.tgz
|
||||
'@rush-temp/inventory': file:./projects/inventory.tgz
|
||||
'@rush-temp/inventory-assets': file:./projects/inventory-assets.tgz
|
||||
'@rush-temp/inventory-resources': file:./projects/inventory-resources.tgz
|
||||
'@rush-temp/lead': file:./projects/lead.tgz
|
||||
'@rush-temp/lead-assets': file:./projects/lead-assets.tgz
|
||||
'@rush-temp/lead-resources': file:./projects/lead-resources.tgz
|
||||
@ -51,6 +54,7 @@ specifiers:
|
||||
'@rush-temp/model-core': file:./projects/model-core.tgz
|
||||
'@rush-temp/model-demo': file:./projects/model-demo.tgz
|
||||
'@rush-temp/model-gmail': file:./projects/model-gmail.tgz
|
||||
'@rush-temp/model-inventory': file:./projects/model-inventory.tgz
|
||||
'@rush-temp/model-lead': file:./projects/model-lead.tgz
|
||||
'@rush-temp/model-recruit': file:./projects/model-recruit.tgz
|
||||
'@rush-temp/model-rig': file:./projects/model-rig.tgz
|
||||
@ -217,6 +221,9 @@ dependencies:
|
||||
'@rush-temp/gmail-resources': file:projects/gmail-resources.tgz_096c09b0b673a57c275d9767a12070b1
|
||||
'@rush-temp/image-cropper': file:projects/image-cropper.tgz
|
||||
'@rush-temp/image-cropper-resources': file:projects/image-cropper-resources.tgz_096c09b0b673a57c275d9767a12070b1
|
||||
'@rush-temp/inventory': file:projects/inventory.tgz
|
||||
'@rush-temp/inventory-assets': file:projects/inventory-assets.tgz
|
||||
'@rush-temp/inventory-resources': file:projects/inventory-resources.tgz_096c09b0b673a57c275d9767a12070b1
|
||||
'@rush-temp/lead': file:projects/lead.tgz
|
||||
'@rush-temp/lead-assets': file:projects/lead-assets.tgz
|
||||
'@rush-temp/lead-resources': file:projects/lead-resources.tgz_096c09b0b673a57c275d9767a12070b1
|
||||
@ -232,6 +239,7 @@ dependencies:
|
||||
'@rush-temp/model-core': file:projects/model-core.tgz_typescript@4.5.4
|
||||
'@rush-temp/model-demo': file:projects/model-demo.tgz_typescript@4.5.4
|
||||
'@rush-temp/model-gmail': file:projects/model-gmail.tgz_typescript@4.5.4
|
||||
'@rush-temp/model-inventory': file:projects/model-inventory.tgz_typescript@4.5.4
|
||||
'@rush-temp/model-lead': file:projects/model-lead.tgz_typescript@4.5.4
|
||||
'@rush-temp/model-recruit': file:projects/model-recruit.tgz_typescript@4.5.4
|
||||
'@rush-temp/model-rig': file:projects/model-rig.tgz_37f79b97d0d86442e45d380c86f520c5
|
||||
@ -11436,6 +11444,82 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/inventory-assets.tgz:
|
||||
resolution: {integrity: sha512-z58zb5383o9j4S1y3tBAYdcGXdI9IORHbh5w+z/lI2PnS41B+HK1JsIrlrep+XG/w+xyAWn3OHlw77VJMQEDNw==, tarball: file:projects/inventory-assets.tgz}
|
||||
name: '@rush-temp/inventory-assets'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.41.8
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@types/node': 16.11.14
|
||||
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
|
||||
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
prettier: 2.5.1
|
||||
typescript: 4.5.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/inventory-resources.tgz_096c09b0b673a57c275d9767a12070b1:
|
||||
resolution: {integrity: sha512-PiKSp7YMOeYnOnyDukPG1Qno6QmImWvc3qBJUY5ZKXiQyZWA9JpWqJm3WfoaprfoLVKCiSvqkA2r2XrqfIjJcQ==, tarball: file:projects/inventory-resources.tgz}
|
||||
id: file:projects/inventory-resources.tgz
|
||||
name: '@rush-temp/inventory-resources'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
|
||||
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
eslint-plugin-svelte3: 3.2.1_eslint@7.32.0+svelte@3.44.3
|
||||
prettier: 2.5.1
|
||||
prettier-plugin-svelte: 2.5.1_prettier@2.5.1+svelte@3.44.3
|
||||
sass: 1.45.0
|
||||
svelte: 3.44.3
|
||||
svelte-check: 2.2.11_4374c622c67ed7479ff0e44c29d09bce
|
||||
svelte-loader: 3.1.2_svelte@3.44.3
|
||||
svelte-preprocess: 4.10.1_14d64cad431e31f100de7363af24a44f
|
||||
typescript: 4.5.4
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- coffeescript
|
||||
- less
|
||||
- node-sass
|
||||
- postcss
|
||||
- postcss-load-config
|
||||
- pug
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/inventory.tgz:
|
||||
resolution: {integrity: sha512-gd3ZCKg7WVdifG6BWuXJhwBNgjdyaFyTaWdlH9e+gFI2a9M9HYeIVmYBWDC2X6QP6//G2yv6sykXytwVaMgqKw==, tarball: file:projects/inventory.tgz}
|
||||
name: '@rush-temp/inventory'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.41.8
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
|
||||
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
prettier: 2.5.1
|
||||
typescript: 4.5.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/lead-assets.tgz:
|
||||
resolution: {integrity: sha512-cRYB8PutP6HmaJjoEMLIEyMQEhKAQaCu0w2NJMF5TUW9vokia/22TXsHo1+xEGI1rx2epywbGXet/fL40tdbDw==, tarball: file:projects/lead-assets.tgz}
|
||||
name: '@rush-temp/lead-assets'
|
||||
@ -11580,7 +11664,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/model-all.tgz_typescript@4.5.4:
|
||||
resolution: {integrity: sha512-oKPQz2zoE53ROTtb76Vp43+PatfI+CKrdq0sUleUVaKPfWw74BKTXmqSAkZU1PVnr/DaKY/Gnl9Eo9JrXOUiig==, tarball: file:projects/model-all.tgz}
|
||||
resolution: {integrity: sha512-QqT4D4AgEQSvRi5epmizX6ipSCnOlaklFy5ne2rUIVJ1MU0cLgOfIXdCjkl47VTsT98JLOWSjv7y5X82bRGnHw==, tarball: file:projects/model-all.tgz}
|
||||
id: file:projects/model-all.tgz
|
||||
name: '@rush-temp/model-all'
|
||||
version: 0.0.0
|
||||
@ -11730,6 +11814,27 @@ packages:
|
||||
- typescript
|
||||
dev: false
|
||||
|
||||
file:projects/model-inventory.tgz_typescript@4.5.4:
|
||||
resolution: {integrity: sha512-P+V2OydmT65gpPhJm85tPkIn5uLRKQmu2sMHg/1oRJLXKCeQz1wAgb5ZzlzmgbilrrfchcwYtJHyPANNcnBrdg==, tarball: file:projects/model-inventory.tgz}
|
||||
id: file:projects/model-inventory.tgz
|
||||
name: '@rush-temp/model-inventory'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.41.8
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
|
||||
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
prettier: 2.5.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
dev: false
|
||||
|
||||
file:projects/model-lead.tgz_typescript@4.5.4:
|
||||
resolution: {integrity: sha512-3+Wf+/TdMpbuSoiTaI/ga+1KHSsPd1q9MDtpG6i4oAhxt/48aTZnfOV4nt9hrpWy2MmQWVhK9YJFTaKGIgKwOA==, tarball: file:projects/model-lead.tgz}
|
||||
id: file:projects/model-lead.tgz
|
||||
@ -12154,7 +12259,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/prod.tgz_sass@1.45.0+typescript@4.5.4:
|
||||
resolution: {integrity: sha512-7OeW4OKQlYw/FrF1rSmbwacpTXSVzNnIPCb/mIP/Q4Kc+uxroItvCRRYUqVH/V9u8W7nyTE7CjDM+KrADL/BAg==, tarball: file:projects/prod.tgz}
|
||||
resolution: {integrity: sha512-aYcYW/uvTMNxOOsjG9io+pTV+pyk0a2CIsoJsF8tFnywgIBJb2X39Yj33nmjRvPpYdsaF4ujn0IdbK4IfM0kZw==, tarball: file:projects/prod.tgz}
|
||||
id: file:projects/prod.tgz
|
||||
name: '@rush-temp/prod'
|
||||
version: 0.0.0
|
||||
|
@ -90,6 +90,9 @@
|
||||
"@anticrm/gmail-assets": "~0.6.0",
|
||||
"@anticrm/gmail-resources": "~0.6.0",
|
||||
"@anticrm/image-cropper": "~0.6.0",
|
||||
"@anticrm/image-cropper-resources": "~0.6.0"
|
||||
"@anticrm/image-cropper-resources": "~0.6.0",
|
||||
"@anticrm/inventory": "~0.6.0",
|
||||
"@anticrm/inventory-assets": "~0.6.0",
|
||||
"@anticrm/inventory-resources": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import { leadId } from '@anticrm/lead'
|
||||
import { clientId } from '@anticrm/client'
|
||||
import { gmailId } from '@anticrm/gmail'
|
||||
import { imageCropperId } from '@anticrm/image-cropper'
|
||||
import { inventoryId } from '@anticrm/inventory'
|
||||
|
||||
import '@anticrm/login-assets'
|
||||
import '@anticrm/task-assets'
|
||||
@ -44,6 +45,7 @@ import '@anticrm/telegram-assets'
|
||||
import '@anticrm/lead-assets'
|
||||
import '@anticrm/gmail-assets'
|
||||
import '@anticrm/workbench-assets'
|
||||
import '@anticrm/inventory-assets'
|
||||
|
||||
import { setMetadata } from '@anticrm/platform'
|
||||
export async function configurePlatform() {
|
||||
@ -73,4 +75,5 @@ export async function configurePlatform() {
|
||||
addLocation(attachmentId, () => import(/* webpackChunkName: "attachment" */ '@anticrm/attachment-resources'))
|
||||
addLocation(gmailId, () => import(/* webpackChunkName: "gmail" */ '@anticrm/gmail-resources'))
|
||||
addLocation(imageCropperId, () => import(/* webpackChunkName: "image-cropper" */ '@anticrm/image-cropper-resources'))
|
||||
addLocation(inventoryId, () => import(/* webpackChunkName: "inventory" */ '@anticrm/inventory-resources'))
|
||||
}
|
||||
|
@ -46,6 +46,7 @@
|
||||
"@anticrm/model-activity": "~0.6.0",
|
||||
"@anticrm/model-attachment": "~0.6.0",
|
||||
"@anticrm/model-gmail": "~0.6.0",
|
||||
"@anticrm/model-inventory": "~0.6.0",
|
||||
"@anticrm/core": "~0.6.13"
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import { createModel as telegramModel } from '@anticrm/model-telegram'
|
||||
import { createModel as attachmentModel } from '@anticrm/model-attachment'
|
||||
import { createModel as leadModel } from '@anticrm/model-lead'
|
||||
import { createModel as gmailModel } from '@anticrm/model-gmail'
|
||||
import { createModel as inventoryModel } from '@anticrm/model-inventory'
|
||||
|
||||
import { createModel as serverCoreModel } from '@anticrm/model-server-core'
|
||||
import { createModel as serverChunterModel } from '@anticrm/model-server-chunter'
|
||||
@ -50,6 +51,7 @@ settingModel(builder)
|
||||
telegramModel(builder)
|
||||
leadModel(builder)
|
||||
gmailModel(builder)
|
||||
inventoryModel(builder)
|
||||
|
||||
serverCoreModel(builder)
|
||||
serverChunterModel(builder)
|
||||
|
@ -17,7 +17,7 @@ import type { IntlString } from '@anticrm/platform'
|
||||
import { Builder, Model, Prop, UX, TypeString, TypeTimestamp } from '@anticrm/model'
|
||||
import type { Domain } from '@anticrm/core'
|
||||
import core, { TAttachedDoc } from '@anticrm/model-core'
|
||||
import type { Attachment } from '@anticrm/attachment'
|
||||
import type { Attachment, Photo } from '@anticrm/attachment'
|
||||
import activity from '@anticrm/activity'
|
||||
|
||||
import view from '@anticrm/model-view'
|
||||
@ -46,8 +46,12 @@ export class TAttachment extends TAttachedDoc implements Attachment {
|
||||
lastModified!: number
|
||||
}
|
||||
|
||||
@Model(attachment.class.Photo, attachment.class.Attachment)
|
||||
@UX('Photo' as IntlString)
|
||||
export class TPhoto extends TAttachment implements Photo {}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TAttachment)
|
||||
builder.createModel(TAttachment, TPhoto)
|
||||
|
||||
builder.mixin(attachment.class.Attachment, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: attachment.component.AttachmentPresenter
|
||||
@ -57,6 +61,10 @@ export function createModel (builder: Builder): void {
|
||||
editor: attachment.component.Attachments
|
||||
})
|
||||
|
||||
builder.mixin(attachment.class.Photo, core.class.Class, view.mixin.AttributeEditor, {
|
||||
editor: attachment.component.Photos
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
|
7
models/inventory/.eslintrc.js
Normal file
7
models/inventory/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/model-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
4
models/inventory/.npmignore
Normal file
4
models/inventory/.npmignore
Normal file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
18
models/inventory/config/rig.json
Normal file
18
models/inventory/config/rig.json
Normal file
@ -0,0 +1,18 @@
|
||||
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
|
||||
/**
|
||||
* (Required) The name of the rig package to inherit from.
|
||||
* It should be an NPM package name with the "-rig" suffix.
|
||||
*/
|
||||
"rigPackageName": "@anticrm/model-rig"
|
||||
|
||||
/**
|
||||
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||
* If omitted, then the "default" profile will be used."
|
||||
*/
|
||||
// "rigProfile": "your-profile-name"
|
||||
}
|
40
models/inventory/package.json
Normal file
40
models/inventory/package.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@anticrm/model-inventory",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"build:watch": "tsc",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"lint": "eslint src",
|
||||
"format": "prettier --write src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/model-rig": "~0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"prettier": "^2.4.1",
|
||||
"@rushstack/heft": "^0.41.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/core": "~0.6.11",
|
||||
"@anticrm/model": "~0.6.0",
|
||||
"@anticrm/model-workbench": "~0.6.0",
|
||||
"@anticrm/model-attachment": "~0.6.0",
|
||||
"@anticrm/model-core": "~0.6.0",
|
||||
"@anticrm/ui": "~0.6.0",
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/inventory": "~0.6.0",
|
||||
"@anticrm/inventory-resources": "~0.6.0",
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/workbench": "~0.6.1"
|
||||
}
|
||||
}
|
148
models/inventory/src/index.ts
Normal file
148
models/inventory/src/index.ts
Normal file
@ -0,0 +1,148 @@
|
||||
//
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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.
|
||||
//
|
||||
|
||||
// To help typescript locate view plugin properly
|
||||
import type { Category, Product, Variant } from '@anticrm/inventory'
|
||||
import { Doc, Domain, FindOptions, Ref } from '@anticrm/core'
|
||||
import { Builder, Collection, Model, Prop, TypeRef, TypeString, UX } from '@anticrm/model'
|
||||
import core, { TAttachedDoc } from '@anticrm/model-core'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import type {} from '@anticrm/view'
|
||||
import inventory from './plugin'
|
||||
import workbench from '@anticrm/model-workbench'
|
||||
import view from '@anticrm/view'
|
||||
import attachment from '@anticrm/model-attachment'
|
||||
|
||||
export const DOMAIN_INVENTORY = 'inventory' as Domain
|
||||
@Model(inventory.class.Category, core.class.AttachedDoc, DOMAIN_INVENTORY)
|
||||
@UX(inventory.string.Category, inventory.icon.Categories, undefined, 'name')
|
||||
export class TCategory extends TAttachedDoc implements Category {
|
||||
@Prop(TypeString(), 'Name' as IntlString)
|
||||
name!: string
|
||||
}
|
||||
|
||||
@Model(inventory.class.Product, core.class.AttachedDoc, DOMAIN_INVENTORY)
|
||||
@UX(inventory.string.Product, inventory.icon.Products, undefined, 'name')
|
||||
export class TProduct extends TAttachedDoc implements Product {
|
||||
// We need to declare, to provide property with label
|
||||
@Prop(TypeRef(inventory.class.Category), inventory.string.Category)
|
||||
declare attachedTo: Ref<Category>
|
||||
|
||||
@Prop(TypeString(), 'Name' as IntlString)
|
||||
name!: string
|
||||
|
||||
@Prop(Collection(attachment.class.Photo), attachment.string.Photos)
|
||||
photos?: number
|
||||
|
||||
@Prop(Collection(inventory.class.Variant), inventory.string.Variants)
|
||||
variants?: number
|
||||
|
||||
@Prop(Collection(attachment.class.Attachment), 'Attachments' as IntlString)
|
||||
attachments?: number
|
||||
}
|
||||
|
||||
@Model(inventory.class.Variant, core.class.AttachedDoc, DOMAIN_INVENTORY)
|
||||
@UX(inventory.string.Variant, inventory.icon.Variant, undefined, 'name')
|
||||
export class TVariant extends TAttachedDoc implements Variant {
|
||||
// We need to declare, to provide property with label
|
||||
@Prop(TypeRef(inventory.class.Product), inventory.string.Product)
|
||||
declare attachedTo: Ref<Product>
|
||||
|
||||
@Prop(TypeString(), 'Name' as IntlString)
|
||||
name!: string
|
||||
|
||||
@Prop(TypeString(), inventory.string.SKU)
|
||||
sku!: string
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TCategory, TProduct, TVariant)
|
||||
|
||||
builder.mixin(inventory.class.Category, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: inventory.component.CategoryPresenter
|
||||
})
|
||||
|
||||
builder.mixin(inventory.class.Product, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: inventory.component.ProductPresenter
|
||||
})
|
||||
|
||||
builder.mixin(inventory.class.Variant, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: inventory.component.VariantPresenter
|
||||
})
|
||||
|
||||
builder.mixin(inventory.class.Variant, core.class.Class, view.mixin.AttributeEditor, {
|
||||
editor: inventory.component.Variants
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: inventory.class.Product,
|
||||
descriptor: view.viewlet.Table,
|
||||
open: inventory.component.EditProduct,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
attachedTo: inventory.class.Category
|
||||
}
|
||||
} as FindOptions<Doc>,
|
||||
config: ['', '$lookup.attachedTo', 'modifiedOn']
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
workbench.class.Application,
|
||||
core.space.Model,
|
||||
{
|
||||
label: inventory.string.Inventory,
|
||||
icon: inventory.icon.InventoryApplication,
|
||||
hidden: false,
|
||||
navigatorModel: {
|
||||
specials: [
|
||||
{
|
||||
id: 'Categories',
|
||||
label: inventory.string.Categories,
|
||||
icon: inventory.icon.Categories,
|
||||
component: inventory.component.Categories
|
||||
},
|
||||
{
|
||||
id: 'Products',
|
||||
label: inventory.string.Products,
|
||||
icon: inventory.icon.Products,
|
||||
component: inventory.component.Products
|
||||
}
|
||||
],
|
||||
spaces: []
|
||||
}
|
||||
},
|
||||
inventory.app.Inventory
|
||||
)
|
||||
|
||||
builder.createDoc(view.class.ActionTarget, core.space.Model, {
|
||||
target: inventory.class.Category,
|
||||
action: inventory.action.CreateSubcategory
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Action,
|
||||
core.space.Model,
|
||||
{
|
||||
label: inventory.string.CreateSubcategory,
|
||||
icon: inventory.icon.Categories,
|
||||
action: inventory.actionImpl.CreateSubcategory
|
||||
},
|
||||
inventory.action.CreateSubcategory
|
||||
)
|
||||
}
|
||||
|
||||
export { default } from './plugin'
|
45
models/inventory/src/plugin.ts
Normal file
45
models/inventory/src/plugin.ts
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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.
|
||||
//
|
||||
|
||||
import type { Doc, Ref } from '@anticrm/core'
|
||||
import { inventoryId } from '@anticrm/inventory'
|
||||
import inventory from '@anticrm/inventory-resources/src/plugin'
|
||||
import { mergeIds, Resource } from '@anticrm/platform'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import { Action } from '@anticrm/view'
|
||||
import { Application } from '@anticrm/workbench'
|
||||
|
||||
export default mergeIds(inventoryId, inventory, {
|
||||
app: {
|
||||
Inventory: '' as Ref<Application>
|
||||
},
|
||||
action: {
|
||||
CreateSubcategory: '' as Ref<Action>
|
||||
},
|
||||
actionImpl: {
|
||||
CreateSubcategory: '' as Resource<(object: Doc) => Promise<void>>
|
||||
},
|
||||
component: {
|
||||
Categories: '' as AnyComponent,
|
||||
Products: '' as AnyComponent,
|
||||
CreateProduct: '' as AnyComponent,
|
||||
EditProduct: '' as AnyComponent,
|
||||
CategoryPresenter: '' as AnyComponent,
|
||||
Variants: '' as AnyComponent,
|
||||
ProductPresenter: '' as AnyComponent,
|
||||
VariantPresenter: '' as AnyComponent
|
||||
}
|
||||
})
|
8
models/inventory/tsconfig.json
Normal file
8
models/inventory/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./node_modules/@anticrm/model-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
}
|
||||
}
|
@ -13,7 +13,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import activity from '@anticrm/activity'
|
||||
import type { Doc } from '@anticrm/core'
|
||||
@ -23,6 +22,7 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let title: string
|
||||
export let subtitle: string | undefined = undefined
|
||||
export let icon: Asset | AnySvelteComponent
|
||||
export let fullSize: boolean = true
|
||||
export let rightSection: AnyComponent | undefined = undefined
|
||||
@ -31,50 +31,70 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<div class="overlay" on:click={() => { dispatch('close') }}/>
|
||||
<div
|
||||
class="overlay"
|
||||
on:click={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
/>
|
||||
<div class="dialog-container" class:fullSize>
|
||||
|
||||
{#if fullSize}
|
||||
<div class="leftSection">
|
||||
<div class="flex-between header">
|
||||
<Icon {icon} size={'large'} />
|
||||
<div class="flex-grow ml-4 flex-col">
|
||||
<div class="fs-title">{title}</div>
|
||||
<div class="small-text content-dark-color">Candidate pool name</div>
|
||||
{#if fullSize}
|
||||
<div class="leftSection">
|
||||
<div class="flex-between header">
|
||||
<Icon {icon} size={'large'} />
|
||||
<div class="flex-grow ml-4 flex-col">
|
||||
<div class="fs-title">{title}</div>
|
||||
{#if subtitle}
|
||||
<div class="small-text content-dark-color">{subtitle}</div>
|
||||
{/if}
|
||||
</div>
|
||||
<ActionIcon icon={IconMoreH} size={'medium'} />
|
||||
</div>
|
||||
{#if $$slots.subtitle}<div class="flex-row-center subtitle"><slot name="subtitle" /></div>{/if}
|
||||
<div class="flex-col scroll-container">
|
||||
<div class="flex-col content">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<ActionIcon icon={IconMoreH} size={'medium'} />
|
||||
</div>
|
||||
{#if $$slots.subtitle}<div class="flex-row-center subtitle"><slot name="subtitle" /></div>{/if}
|
||||
<div class="flex-col scroll-container">
|
||||
<div class="flex-col content">
|
||||
<div class="rightSection">
|
||||
<Component is={rightSection ?? activity.component.Activity} props={{ object, fullSize }} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="unionSection">
|
||||
<div class="flex-row-center header">
|
||||
<Icon {icon} size={'large'} />
|
||||
<div class="flex-grow ml-4 flex-col">
|
||||
<div class="fs-title">{title}</div>
|
||||
<div class="small-text content-dark-color">Candidate pool name</div>
|
||||
</div>
|
||||
<ActionIcon icon={IconMoreH} size={'medium'} />
|
||||
</div>
|
||||
{#if $$slots.subtitle}<div class="flex-row-center subtitle"><slot name="subtitle" /></div>{/if}
|
||||
|
||||
<Component is={activity.component.Activity} props={{ object, fullSize }}>
|
||||
<slot />
|
||||
</div>
|
||||
</Component>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rightSection">
|
||||
<Component is={rightSection ?? activity.component.Activity} props={{object, fullSize}}/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="unionSection">
|
||||
<div class="flex-row-center header">
|
||||
<Icon {icon} size={'large'} />
|
||||
<div class="flex-grow ml-4 flex-col">
|
||||
<div class="fs-title">{title}</div>
|
||||
<div class="small-text content-dark-color">Candidate pool name</div>
|
||||
</div>
|
||||
<ActionIcon icon={IconMoreH} size={'medium'} />
|
||||
</div>
|
||||
{#if $$slots.subtitle}<div class="flex-row-center subtitle"><slot name="subtitle" /></div>{/if}
|
||||
|
||||
<Component is={activity.component.Activity} props={{object, fullSize}}>
|
||||
<slot />
|
||||
</Component>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<div class="tools">
|
||||
<div class="tool" on:click={() => { fullSize = !fullSize }}><div class="icon"><IconExpand size={'small'} /></div></div>
|
||||
<div class="tool" on:click={() => { dispatch('close') }}><div class="icon"><IconClose size={'small'} /></div></div>
|
||||
<div
|
||||
class="tool"
|
||||
on:click={() => {
|
||||
fullSize = !fullSize
|
||||
}}
|
||||
>
|
||||
<div class="icon"><IconExpand size={'small'} /></div>
|
||||
</div>
|
||||
<div
|
||||
class="tool"
|
||||
on:click={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<div class="icon"><IconClose size={'small'} /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -116,9 +136,11 @@
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: max-content;
|
||||
|
||||
.header { padding: 0 6rem 0 2.5rem; }
|
||||
height: max-content;
|
||||
|
||||
.header {
|
||||
padding: 0 6rem 0 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.fullSize {
|
||||
@ -126,7 +148,8 @@
|
||||
left: 1rem;
|
||||
}
|
||||
|
||||
.leftSection, .rightSection {
|
||||
.leftSection,
|
||||
.rightSection {
|
||||
flex-basis: 50%;
|
||||
width: 50%;
|
||||
min-height: 0;
|
||||
@ -140,7 +163,7 @@
|
||||
margin: 2.5rem 2rem 1.5rem;
|
||||
.content {
|
||||
flex-shrink: 0;
|
||||
margin: .5rem .5rem 0;
|
||||
margin: 0.5rem 0.5rem 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -158,7 +181,9 @@
|
||||
margin-left: 1rem;
|
||||
color: var(--theme-content-accent-color);
|
||||
cursor: pointer;
|
||||
&:hover { color: var(--theme-caption-color); }
|
||||
&:hover {
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,4 +196,4 @@
|
||||
background: #000;
|
||||
opacity: .5;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -3,6 +3,7 @@
|
||||
"UploadDropFilesHere": "Upload or drop files here",
|
||||
"NoAttachments": "There are no attachments for this",
|
||||
"AddAttachment": "uploaded an attachment",
|
||||
"Attachments": "Attachments"
|
||||
"Attachments": "Attachments",
|
||||
"Photos": "Photos"
|
||||
}
|
||||
}
|
195
plugins/attachment-resources/src/components/Photos.svelte
Normal file
195
plugins/attachment-resources/src/components/Photos.svelte
Normal file
@ -0,0 +1,195 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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 { Photo } from '@anticrm/attachment'
|
||||
import { Class, Doc, Ref, Space } from '@anticrm/core'
|
||||
import { setPlatformStatus, unknownError } from '@anticrm/platform'
|
||||
import { createQuery, getClient, getFileUrl, PDFViewer } from '@anticrm/presentation'
|
||||
import { CircleButton, IconAdd, Label, showPopup, Spinner } from '@anticrm/ui'
|
||||
import attachment from '../plugin'
|
||||
import { uploadFile } from '../utils'
|
||||
import UploadDuo from './icons/UploadDuo.svelte'
|
||||
|
||||
export let objectId: Ref<Doc>
|
||||
export let space: Ref<Space>
|
||||
export let _class: Ref<Class<Doc>>
|
||||
|
||||
let inputFile: HTMLInputElement
|
||||
let loading = 0
|
||||
let images: Photo[] = []
|
||||
|
||||
const client = getClient()
|
||||
const query = createQuery()
|
||||
query.query(
|
||||
attachment.class.Photo,
|
||||
{
|
||||
attachedTo: objectId
|
||||
},
|
||||
(res) => {
|
||||
images = res
|
||||
}
|
||||
)
|
||||
|
||||
async function create (file: File) {
|
||||
if (!file.type.startsWith('image/')) return
|
||||
loading++
|
||||
try {
|
||||
const uuid = await uploadFile(file, space, objectId)
|
||||
console.log('uploaded file uuid', uuid)
|
||||
client.addCollection(attachment.class.Photo, space, objectId, _class, 'photos', {
|
||||
name: file.name,
|
||||
file: uuid,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
lastModified: file.lastModified
|
||||
})
|
||||
} catch (err: any) {
|
||||
setPlatformStatus(unknownError(err))
|
||||
} finally {
|
||||
loading--
|
||||
}
|
||||
}
|
||||
|
||||
function fileSelected () {
|
||||
const list = inputFile.files
|
||||
if (list === null || list.length === 0) return
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const file = list.item(index)
|
||||
if (file !== null) create(file)
|
||||
}
|
||||
}
|
||||
|
||||
function fileDrop (e: DragEvent) {
|
||||
const list = e.dataTransfer?.files
|
||||
if (list === undefined || list.length === 0) return
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const file = list.item(index)
|
||||
if (file !== null) create(file)
|
||||
}
|
||||
}
|
||||
|
||||
let dragover = false
|
||||
|
||||
function click (ev: Event, item?: Photo): void {
|
||||
const el: HTMLElement = ev.currentTarget as HTMLElement
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' })
|
||||
if (item !== undefined) {
|
||||
showPopup(PDFViewer, { file: item.file, name: item.name }, 'right')
|
||||
} else {
|
||||
inputFile.click()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="attachments-container">
|
||||
<div class="flex-row-center">
|
||||
<span class="title"><Label label={attachment.string.Photos} /></span>
|
||||
{#if loading}
|
||||
<Spinner />
|
||||
{:else}
|
||||
<CircleButton
|
||||
icon={IconAdd}
|
||||
size={'small'}
|
||||
selected
|
||||
on:click={() => {
|
||||
inputFile.click()
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<input
|
||||
bind:this={inputFile}
|
||||
multiple
|
||||
type="file"
|
||||
name="file"
|
||||
accept="image/*"
|
||||
id="file"
|
||||
style="display: none"
|
||||
on:change={fileSelected}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex-row-center mt-5 zone-container"
|
||||
class:solid={dragover}
|
||||
on:dragover|preventDefault={() => {
|
||||
dragover = true
|
||||
}}
|
||||
on:dragleave={() => {
|
||||
dragover = false
|
||||
}}
|
||||
on:drop|preventDefault|stopPropagation={fileDrop}
|
||||
>
|
||||
{#each images as image (image._id)}
|
||||
<div
|
||||
class="item"
|
||||
on:click={(ev) => {
|
||||
click(ev, image)
|
||||
}}
|
||||
>
|
||||
<img src={getFileUrl(image.file)} alt={image.name} />
|
||||
</div>
|
||||
{/each}
|
||||
<div class="flex-center item new-item" on:click={click}>
|
||||
<UploadDuo size={'large'} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.attachments-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
margin-right: 0.75rem;
|
||||
font-weight: 500;
|
||||
font-size: 1.25rem;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
}
|
||||
|
||||
.zone-container {
|
||||
padding: 1rem;
|
||||
color: var(--theme-caption-color);
|
||||
background: var(--theme-bg-accent-color);
|
||||
border: 1px solid var(--theme-bg-focused-color);
|
||||
border-radius: 0.75rem;
|
||||
overflow-x: auto;
|
||||
|
||||
.item {
|
||||
width: 5rem;
|
||||
min-width: 5rem;
|
||||
height: 5rem;
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.item + .item {
|
||||
margin-left: 0.625rem;
|
||||
}
|
||||
|
||||
.new-item {
|
||||
background: var(--theme-bg-accent-color);
|
||||
border: 1px dashed var(--theme-zone-border-lite);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,14 +1,14 @@
|
||||
//
|
||||
// Copyright © 2020 Anticrm Platform Contributors.
|
||||
//
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
@ -17,15 +17,18 @@ import AttachmentsPresenter from './components/AttachmentsPresenter.svelte'
|
||||
import AttachmentPresenter from './components/AttachmentPresenter.svelte'
|
||||
import TxAttachmentCreate from './components/activity/TxAttachmentCreate.svelte'
|
||||
import Attachments from './components/Attachments.svelte'
|
||||
import Photos from './components/Photos.svelte'
|
||||
import { Resources } from '@anticrm/platform'
|
||||
import { uploadFile, deleteFile } from './utils'
|
||||
|
||||
export { Attachments, AttachmentsPresenter }
|
||||
|
||||
export default async () => ({
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
AttachmentsPresenter,
|
||||
AttachmentPresenter,
|
||||
Attachments
|
||||
Attachments,
|
||||
Photos
|
||||
},
|
||||
activity: {
|
||||
TxAttachmentCreate
|
||||
|
@ -1,14 +1,15 @@
|
||||
//
|
||||
// Copyright © 2020 Anticrm Platform Contributors.
|
||||
//
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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.
|
||||
//
|
||||
@ -22,6 +23,7 @@ export default mergeIds(attachmentId, attachment, {
|
||||
string: {
|
||||
NoAttachments: '' as IntlString,
|
||||
UploadDropFilesHere: '' as IntlString,
|
||||
Attachments: '' as IntlString
|
||||
Attachments: '' as IntlString,
|
||||
Photos: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
@ -30,6 +30,11 @@ export interface Attachment extends AttachedDoc {
|
||||
lastModified: number
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Photo extends Attachment {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -37,13 +42,15 @@ export const attachmentId = 'attachment' as Plugin
|
||||
|
||||
export default plugin(attachmentId, {
|
||||
component: {
|
||||
Attachments: '' as AnyComponent
|
||||
Attachments: '' as AnyComponent,
|
||||
Photos: '' as AnyComponent
|
||||
},
|
||||
icon: {
|
||||
Attachment: '' as Asset
|
||||
},
|
||||
class: {
|
||||
Attachment: '' as Ref<Class<Attachment>>
|
||||
Attachment: '' as Ref<Class<Attachment>>,
|
||||
Photo: '' as Ref<Class<Photo>>
|
||||
},
|
||||
helper: {
|
||||
UploadFile: '' as Resource<(file: File, space?: Ref<Space>, attachedTo?: Ref<Doc>) => Promise<string>>,
|
||||
|
21
plugins/inventory-assets/assets/icons.svg
Normal file
21
plugins/inventory-assets/assets/icons.svg
Normal file
@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||
<symbol id="inventory" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M13.1716 3H9C7.11438 3 6.17157 3 5.58579 3.58579C5 4.17157 5 5.11438 5 7V17C5 18.8856 5 19.8284 5.58579 20.4142C6.17157 21 7.11438 21 9 21H15C16.8856 21 17.8284 21 18.4142 20.4142C19 19.8284 19 18.8856 19 17V8.82843C19 8.41968 19 8.2153 18.9239 8.03153C18.8478 7.84776 18.7032 7.70324 18.4142 7.41421L14.5858 3.58579C14.2968 3.29676 14.1522 3.15224 13.9685 3.07612C13.7847 3 13.5803 3 13.1716 3Z" stroke="currentColor" stroke-width="1.3"/>
|
||||
<path d="M9 13L15 13" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
|
||||
<path d="M9 17L13 17" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
|
||||
<path d="M13 3V7C13 7.94281 13 8.41421 13.2929 8.70711C13.5858 9 14.0572 9 15 9H19" stroke="currentColor" stroke-width="1.3"/></symbol>
|
||||
<symbol id="categories" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M2 6.66669C2 4.78107 2 3.83826 2.58579 3.25247C3.17157 2.66669 4.11438 2.66669 6 2.66669H10C11.8856 2.66669 12.8284 2.66669 13.4142 3.25247C14 3.83826 14 4.78107 14 6.66669V9.33335C14 11.219 14 12.1618 13.4142 12.7476C12.8284 13.3334 11.8856 13.3334 10 13.3334H6C4.11438 13.3334 3.17157 13.3334 2.58579 12.7476C2 12.1618 2 11.219 2 9.33335V6.66669Z" stroke="white"/>
|
||||
<path d="M5.33398 2.66669V2.66669C5.42787 2.66669 5.47482 2.66669 5.51834 2.66858C6.21284 2.69886 6.8418 3.08757 7.17946 3.69521C7.20062 3.73329 7.22161 3.77528 7.2636 3.85926L7.75628 4.8446C8.10324 5.53853 8.27672 5.88549 8.54248 6.13227C8.73342 6.30957 8.95717 6.44786 9.20115 6.53935C9.54072 6.66669 9.92864 6.66669 10.7045 6.66669V6.66669C11.9155 6.66669 12.5209 6.66669 12.9753 6.92023C13.2991 7.10097 13.5664 7.3682 13.7471 7.69206C14.0007 8.1464 14.0007 8.75189 14.0007 9.96287V10.6667" stroke="white"/>
|
||||
</symbol>
|
||||
<symbol id="products" viewBox="0 0 16 16" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.54693 13.1966L2.80604 9.45673C2.6101 9.26106 2.5 8.99554 2.5 8.71866C2.5 8.44178 2.6101 8.17626 2.80604 7.98059L8.28258 2.5L13.5 2.5L13.5 7.71605L8.01824 13.1966C7.61124 13.6011 6.95392 13.6011 6.54693 13.1966Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</symbol>
|
||||
<symbol id="variant" viewBox="0 0 16 16" fill="none">
|
||||
<rect x="12" y="6" width="2.66667" height="2.66667" rx="1.33333" transform="rotate(90 12 6)" stroke="currentColor"/>
|
||||
<rect x="12" y="11.3333" width="2.66667" height="2.66667" rx="1.33333" transform="rotate(90 12 11.3333)" stroke="currentColor"/>
|
||||
<rect x="2" y="4.66675" width="2.66667" height="2.66667" rx="1.33333" transform="rotate(-90 2 4.66675)" stroke="currentColor"/>
|
||||
<path d="M3.3335 5.33325V8.66658C3.3335 10.5522 3.3335 11.495 3.91928 12.0808C4.50507 12.6666 5.44788 12.6666 7.3335 12.6666H9.3335" stroke="currentColor"/>
|
||||
<path d="M3.3335 4.66675V4.66675C3.3335 5.28673 3.3335 5.59672 3.40164 5.85105C3.58658 6.54124 4.12567 7.08033 4.81586 7.26527C5.07019 7.33342 5.38018 7.33342 6.00016 7.33342H9.3335" stroke="currentColor"/>
|
||||
</symbol>
|
||||
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
23
plugins/inventory-assets/lang/en.json
Normal file
23
plugins/inventory-assets/lang/en.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"status": {
|
||||
"CategoryRequired": "Category required",
|
||||
"NameRequired": "Name required"
|
||||
},
|
||||
"string": {
|
||||
"Categories": "Categories",
|
||||
"Category": "Category",
|
||||
"CreateCategoryShort": "+ Category",
|
||||
"CreateCategory": "Create category",
|
||||
"CreateSubcategory": "Create subcategory",
|
||||
"Inventory": "Inventory",
|
||||
"CreateProductShort": "+ Product",
|
||||
"CreateProduct": "Create product",
|
||||
"Products": "Products",
|
||||
"Product": "Product",
|
||||
"Variant": "Variant",
|
||||
"SKU": "SKU",
|
||||
"Variants": "Variants",
|
||||
"NoVariantsForProduct": "There are no variants for this product.",
|
||||
"CreateVariant": "Create variant"
|
||||
}
|
||||
}
|
33
plugins/inventory-assets/package.json
Normal file
33
plugins/inventory-assets/package.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "@anticrm/inventory-assets",
|
||||
"version": "0.6.0",
|
||||
"main": "src/index.ts",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"format": "prettier --write src && eslint --fix src",
|
||||
"build:watch": "tsc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/platform-rig": "~0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint": "^7.32.0",
|
||||
"prettier": "^2.4.1",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@rushstack/heft": "^0.41.1",
|
||||
"typescript": "^4.3.5",
|
||||
"@types/node": "^16.4.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/inventory": "~0.6.0"
|
||||
}
|
||||
}
|
27
plugins/inventory-assets/src/index.ts
Normal file
27
plugins/inventory-assets/src/index.ts
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { addStringsLoader, loadMetadata } from '@anticrm/platform'
|
||||
import inventory, { inventoryId } from '@anticrm/inventory'
|
||||
|
||||
const icons = require('../assets/icons.svg')
|
||||
loadMetadata(inventory.icon, {
|
||||
InventoryApplication: `${icons}#inventory`,
|
||||
Categories: `${icons}#categories`,
|
||||
Variant: `${icons}#variant`,
|
||||
Products: `${icons}#products`
|
||||
})
|
||||
addStringsLoader(inventoryId, async (lang: string) => await import(`../lang/${lang}.json`))
|
15
plugins/inventory-assets/tsconfig.json
Normal file
15
plugins/inventory-assets/tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"declaration": true,
|
||||
"outDir": "./lib",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom"
|
||||
]
|
||||
}
|
||||
}
|
7
plugins/inventory-resources/.eslintrc.js
Normal file
7
plugins/inventory-resources/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/ui/config/eslint.config.json'],
|
||||
parserOptions: { tsconfigRootDir: __dirname },
|
||||
settings: {
|
||||
'svelte3/ignore-styles': () => true
|
||||
}
|
||||
}
|
43
plugins/inventory-resources/package.json
Normal file
43
plugins/inventory-resources/package.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@anticrm/inventory-resources",
|
||||
"version": "0.6.0",
|
||||
"main": "src/index.ts",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "echo 'no build for ui'",
|
||||
"build:docs": "api-extractor run --local",
|
||||
"lint": "svelte-check && eslint",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"format": "prettier --write --plugin-search-dir=. src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/platform-rig": "~0.6.0",
|
||||
"svelte-loader": "^3.1.2",
|
||||
"sass": "^1.37.5",
|
||||
"svelte-preprocess": "^4.7.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"eslint-plugin-svelte3": "~3.2.1",
|
||||
"prettier-plugin-svelte": "^2.2.0",
|
||||
"prettier": "^2.4.1",
|
||||
"svelte-check": "^2.2.10",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"svelte": "^3.37.0",
|
||||
"@anticrm/inventory": "~0.6.0",
|
||||
"@anticrm/ui": "~0.6.0",
|
||||
"@anticrm/panel": "~0.6.0",
|
||||
"@anticrm/presentation": "~0.6.2",
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/view-resources": "~0.6.0",
|
||||
"@anticrm/core": "~0.6.11"
|
||||
}
|
||||
}
|
5
plugins/inventory-resources/postcss.config.js
Normal file
5
plugins/inventory-resources/postcss.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('autoprefixer')
|
||||
]
|
||||
}
|
114
plugins/inventory-resources/src/components/Categories.svelte
Normal file
114
plugins/inventory-resources/src/components/Categories.svelte
Normal file
@ -0,0 +1,114 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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 { Button, EditWithIcon, Icon, IconSearch, Label, ScrollBox, showPopup } from '@anticrm/ui'
|
||||
import HierarchyView from './HierarchyView.svelte'
|
||||
import CreateCategory from './CreateCategory.svelte'
|
||||
import inventory from '../plugin'
|
||||
|
||||
let search = ''
|
||||
$: resultQuery = search === '' ? {} : { $search: search }
|
||||
|
||||
function showCreateDialog (ev: Event) {
|
||||
showPopup(CreateCategory, { space: inventory.space.Category }, ev.target as HTMLElement)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="categories-header-container">
|
||||
<div class="header-container">
|
||||
<div class="flex-row-center">
|
||||
<span class="icon"><Icon icon={inventory.icon.Categories} size={'small'} /></span>
|
||||
<span class="label"><Label label={inventory.string.Categories} /></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<EditWithIcon
|
||||
icon={IconSearch}
|
||||
placeholder={'Search'}
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
resultQuery = {}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label={inventory.string.CreateCategoryShort}
|
||||
primary={true}
|
||||
size={'small'}
|
||||
on:click={(ev) => showCreateDialog(ev)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="panel-component">
|
||||
<ScrollBox vertical stretch noShift>
|
||||
<HierarchyView _class={inventory.class.Category} config={['', 'modifiedOn']} options={{}} query={resultQuery} />
|
||||
</ScrollBox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
padding-bottom: 1.25rem;
|
||||
|
||||
.panel-component {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-right: 1rem;
|
||||
height: 100%;
|
||||
border-radius: 1.25rem;
|
||||
background-color: var(--theme-bg-color);
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
.categories-header-container {
|
||||
display: grid;
|
||||
grid-template-columns: auto;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: min-content;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
padding: 0 1.75rem 0 2.5rem;
|
||||
height: 4rem;
|
||||
min-height: 4rem;
|
||||
white-space: nowrap;
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
|
||||
.icon {
|
||||
margin-right: 0.5rem;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.label {
|
||||
flex-grow: 1;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
max-width: 35rem;
|
||||
}
|
||||
.label {
|
||||
font-weight: 500;
|
||||
font-size: 1rem;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,26 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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 { Category } from '@anticrm/inventory'
|
||||
|
||||
export let value: Category
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<div class="overflow-label sm-tool-icon">
|
||||
{value.name}
|
||||
</div>
|
||||
{/if}
|
@ -0,0 +1,71 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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 { AttachedData, Doc, Ref } from '@anticrm/core'
|
||||
import { generateId } from '@anticrm/core'
|
||||
import { OK, Status } from '@anticrm/platform'
|
||||
import { Card, getClient } from '@anticrm/presentation'
|
||||
import type { Category } from '@anticrm/inventory'
|
||||
import { EditBox, Grid, Status as StatusControl } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import inventory from '../plugin'
|
||||
|
||||
const status: Status = OK
|
||||
|
||||
export let attachedTo: Ref<Doc> = inventory.global.Category
|
||||
|
||||
let name: string = ''
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const inventoryId = generateId() as Ref<Category>
|
||||
|
||||
export function canClose (): boolean {
|
||||
return name !== ''
|
||||
}
|
||||
|
||||
async function create () {
|
||||
const value: AttachedData<Category> = {
|
||||
name
|
||||
}
|
||||
|
||||
await client.addCollection(
|
||||
inventory.class.Category,
|
||||
inventory.space.Category,
|
||||
attachedTo,
|
||||
inventory.class.Category,
|
||||
'categories',
|
||||
value,
|
||||
inventoryId
|
||||
)
|
||||
dispatch('close')
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={inventory.string.CreateCategory}
|
||||
okAction={create}
|
||||
space={inventory.space.Category}
|
||||
canSave={name.length > 0}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<StatusControl slot="error" {status} />
|
||||
<Grid column={1} rowGap={1.5}>
|
||||
<EditBox label={inventory.string.Category} bind:value={name} placeholder="Category" maxWidth={'16rem'} focus />
|
||||
</Grid>
|
||||
</Card>
|
@ -0,0 +1,90 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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 { Account, generateId, Ref } from '@anticrm/core'
|
||||
import { Card, createQuery, getClient } from '@anticrm/presentation'
|
||||
import { DropdownLabels, EditBox, Grid } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import inventory from '../plugin'
|
||||
import { Category, Product } from '@anticrm/inventory'
|
||||
import { DropdownTextItem } from '@anticrm/ui/src/types'
|
||||
|
||||
const doc: Product = {
|
||||
name: '',
|
||||
attachedTo: undefined,
|
||||
attachedToClass: inventory.class.Category,
|
||||
_class: inventory.class.Product,
|
||||
space: inventory.space.Products,
|
||||
_id: generateId(),
|
||||
collection: 'products',
|
||||
modifiedOn: Date.now(),
|
||||
modifiedBy: '' as Ref<Account>
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
export function canClose (): boolean {
|
||||
return doc.attachedTo.length === 0 && doc.name.length === 0
|
||||
}
|
||||
|
||||
async function create () {
|
||||
const categoryInstance = await client.findOne(inventory.class.Category, { _id: doc.attachedTo as Ref<Category> })
|
||||
if (categoryInstance === undefined) {
|
||||
throw new Error('category not found')
|
||||
}
|
||||
|
||||
await client.addCollection(
|
||||
inventory.class.Product,
|
||||
doc.space,
|
||||
doc.attachedTo,
|
||||
categoryInstance._class,
|
||||
'products',
|
||||
{
|
||||
name: doc.name
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let categories: DropdownTextItem[] = []
|
||||
const categoriesQ = createQuery()
|
||||
$: categoriesQ.query(inventory.class.Category, {}, (result) => {
|
||||
categories = result.map((c) => {
|
||||
return { id: c._id, label: c.name }
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={inventory.string.CreateProduct}
|
||||
okAction={create}
|
||||
canSave={doc.name.trim().length > 0 && doc.attachedTo !== undefined}
|
||||
bind:space={doc.space}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<Grid column={1} rowGap={1.75}>
|
||||
<EditBox label={inventory.string.Product} bind:value={doc.name} placeholder="Product" maxWidth={'16rem'} focus />
|
||||
<DropdownLabels
|
||||
header
|
||||
items={categories}
|
||||
bind:selected={doc.attachedTo}
|
||||
caption={inventory.string.Categories}
|
||||
title={inventory.string.Category}
|
||||
/>
|
||||
</Grid>
|
||||
</Card>
|
@ -0,0 +1,72 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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 { Account, generateId, Ref } from '@anticrm/core'
|
||||
import { Card, getClient } from '@anticrm/presentation'
|
||||
import { EditBox, Grid } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import inventory from '../plugin'
|
||||
import { Product, Variant } from '@anticrm/inventory'
|
||||
|
||||
export let product: Ref<Product>
|
||||
|
||||
const doc: Variant = {
|
||||
name: '',
|
||||
sku: '',
|
||||
attachedTo: product,
|
||||
attachedToClass: inventory.class.Product,
|
||||
_class: inventory.class.Variant,
|
||||
space: inventory.space.Products,
|
||||
_id: generateId(),
|
||||
collection: 'variants',
|
||||
modifiedOn: Date.now(),
|
||||
modifiedBy: '' as Ref<Account>
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
export function canClose (): boolean {
|
||||
return doc.attachedTo.length === 0 && doc.name.length === 0
|
||||
}
|
||||
|
||||
async function create () {
|
||||
const productInstance = await client.findOne(inventory.class.Product, { _id: doc.attachedTo as Ref<Product> })
|
||||
if (productInstance === undefined) {
|
||||
throw new Error('product not found')
|
||||
}
|
||||
|
||||
await client.addCollection(inventory.class.Variant, doc.space, doc.attachedTo, productInstance._class, 'variants', {
|
||||
name: doc.name,
|
||||
sku: doc.sku
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={inventory.string.CreateVariant}
|
||||
okAction={create}
|
||||
canSave={doc.name.trim().length > 0 && doc.sku.trim().length > 0}
|
||||
bind:space={doc.space}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<Grid column={1} rowGap={1.75}>
|
||||
<EditBox label={inventory.string.Variant} bind:value={doc.name} placeholder="Variant" maxWidth={'16rem'} focus />
|
||||
<EditBox label={inventory.string.SKU} bind:value={doc.sku} placeholder="SKU" maxWidth={'16rem'} />
|
||||
</Grid>
|
||||
</Card>
|
152
plugins/inventory-resources/src/components/EditProduct.svelte
Normal file
152
plugins/inventory-resources/src/components/EditProduct.svelte
Normal file
@ -0,0 +1,152 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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 core, { Doc, Ref } from '@anticrm/core'
|
||||
import { Category, Product } from '@anticrm/inventory'
|
||||
import { Panel } from '@anticrm/panel'
|
||||
import {
|
||||
AttributesBar,
|
||||
createQuery,
|
||||
getAttributePresenterClass,
|
||||
getClient,
|
||||
KeyedAttribute
|
||||
} from '@anticrm/presentation'
|
||||
import { AnyComponent, Component } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import inventory from '../plugin'
|
||||
|
||||
export let _id: Ref<Product>
|
||||
let object: Product
|
||||
let rightSection: AnyComponent | undefined
|
||||
const fullSize: boolean = true
|
||||
let category: Category | undefined
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
||||
|
||||
const query = createQuery()
|
||||
$: _id && update(_id)
|
||||
|
||||
function update (id: Ref<Product>) {
|
||||
query.query(inventory.class.Product, { _id: id }, (result) => {
|
||||
object = result[0]
|
||||
client.findOne(inventory.class.Category, { _id: object.attachedTo as Ref<Category> }).then((res) => {
|
||||
category = res
|
||||
})
|
||||
updateKeys(['comments'])
|
||||
})
|
||||
}
|
||||
|
||||
let keys: KeyedAttribute[] = []
|
||||
let collectionKeys: KeyedAttribute[] = []
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function filterKeys (keys: KeyedAttribute[], ignoreKeys: string[]): KeyedAttribute[] {
|
||||
keys = keys.filter((k) => !docKeys.has(k.key))
|
||||
keys = keys.filter((k) => !ignoreKeys.includes(k.key))
|
||||
return keys
|
||||
}
|
||||
|
||||
function getFiltredKeys (ignoreKeys: string[]): KeyedAttribute[] {
|
||||
const keys = [...hierarchy.getAllAttributes(object._class).entries()]
|
||||
.filter(([, value]) => value.hidden !== true)
|
||||
.map(([key, attr]) => ({ key, attr }))
|
||||
|
||||
return filterKeys(keys, ignoreKeys)
|
||||
}
|
||||
|
||||
function updateKeys (ignoreKeys: string[]): void {
|
||||
const filtredKeys = getFiltredKeys(ignoreKeys)
|
||||
keys = collectionsFilter(filtredKeys, false)
|
||||
collectionKeys = collectionsFilter(filtredKeys, true)
|
||||
}
|
||||
|
||||
function collectionsFilter (keys: KeyedAttribute[], get: boolean): KeyedAttribute[] {
|
||||
const result: KeyedAttribute[] = []
|
||||
for (const key of keys) {
|
||||
if (isCollectionAttr(key) === get) result.push(key)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function isCollectionAttr (key: KeyedAttribute): boolean {
|
||||
return hierarchy.isDerived(key.attr.type._class, core.class.Collection)
|
||||
}
|
||||
|
||||
async function getCollectionEditor (key: KeyedAttribute): Promise<AnyComponent> {
|
||||
const attrClass = getAttributePresenterClass(key.attr)
|
||||
const clazz = client.getHierarchy().getClass(attrClass)
|
||||
const editorMixin = client.getHierarchy().as(clazz, view.mixin.AttributeEditor)
|
||||
return editorMixin.editor
|
||||
}
|
||||
|
||||
function getCollectionCounter (object: Doc, key: KeyedAttribute): number {
|
||||
if (client.getHierarchy().isMixin(key.attr.attributeOf)) {
|
||||
return (client.getHierarchy().as(object, key.attr.attributeOf) as any)[key.key]
|
||||
}
|
||||
return (object as any)[key.key] ?? 0
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if object !== undefined}
|
||||
<Panel
|
||||
icon={inventory.icon.Products}
|
||||
title={object.name}
|
||||
subtitle={category?.name}
|
||||
{rightSection}
|
||||
{fullSize}
|
||||
{object}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<div slot="subtitle">
|
||||
{#if keys}
|
||||
<AttributesBar {object} {keys} />
|
||||
{/if}
|
||||
</div>
|
||||
{#each collectionKeys as collection}
|
||||
<div class="collection">
|
||||
{#await getCollectionEditor(collection) then is}
|
||||
<Component
|
||||
{is}
|
||||
props={{
|
||||
objectId: object._id,
|
||||
_class: object._class,
|
||||
space: object.space,
|
||||
[collection.key]: getCollectionCounter(object, collection)
|
||||
}}
|
||||
/>
|
||||
{/await}
|
||||
</div>
|
||||
{/each}
|
||||
</Panel>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.main-editor {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.collection + .collection {
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,139 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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 { Doc, Ref } from '@anticrm/core'
|
||||
import { IconMoreV, showPopup } from '@anticrm/ui'
|
||||
import { AttributeModel } from '@anticrm/view'
|
||||
import inventory, { Category } from '@anticrm/inventory'
|
||||
import HierarchyElement from './HierarchyElement.svelte'
|
||||
import { ContextMenu } from '@anticrm/view-resources'
|
||||
import Expand from './icons/Expand.svelte'
|
||||
import Collapse from './icons/Collapse.svelte'
|
||||
|
||||
export let descendants: Map<Ref<Doc>, Category[]>
|
||||
export let level: number = 1
|
||||
export let model: AttributeModel[]
|
||||
export let parent: Ref<Doc> = inventory.global.Category
|
||||
let expanded: Set<Ref<Category>> = new Set<Ref<Category>>()
|
||||
|
||||
function getValue (doc: Category, key: string): any {
|
||||
if (key.length === 0) {
|
||||
return doc
|
||||
}
|
||||
const path = key.split('.')
|
||||
const len = path.length
|
||||
let obj = doc as any
|
||||
for (let i = 0; i < len; i++) {
|
||||
obj = obj?.[path[i]]
|
||||
}
|
||||
return obj ?? ''
|
||||
}
|
||||
|
||||
const showMenu = async (ev: MouseEvent, object: Category): Promise<void> => {
|
||||
showPopup(ContextMenu, { object }, ev.target as HTMLElement)
|
||||
}
|
||||
|
||||
function click (id: Ref<Category>): void {
|
||||
if (!descendants.has(id)) return
|
||||
if (expanded.has(id)) {
|
||||
expanded.delete(id)
|
||||
} else {
|
||||
expanded.add(id)
|
||||
}
|
||||
expanded = expanded
|
||||
}
|
||||
|
||||
$: style = `margin-left: ${level * 1.5}rem;`
|
||||
</script>
|
||||
|
||||
{#each descendants.get(parent) ?? [] as object, row (object._id)}
|
||||
<tr class="tr-body">
|
||||
{#each model as attribute, cell}
|
||||
{#if !cell}
|
||||
<td>
|
||||
<div class="firstCell" {style}>
|
||||
{#if descendants.has(object._id)}
|
||||
<div class="expand" on:click={(ev) => click(object._id)}>
|
||||
{#if expanded.has(object._id)}
|
||||
<Expand size={'small'} />
|
||||
{:else}
|
||||
<Collapse size={'small'} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<svelte:component this={attribute.presenter} value={getValue(object, attribute.key)} {...attribute.props} />
|
||||
<div class="menuRow" on:click={(ev) => showMenu(ev, object)}><IconMoreV size={'small'} /></div>
|
||||
</div>
|
||||
</td>
|
||||
{:else}
|
||||
<td>
|
||||
<svelte:component this={attribute.presenter} value={getValue(object, attribute.key)} {...attribute.props} />
|
||||
</td>
|
||||
{/if}
|
||||
{/each}
|
||||
</tr>
|
||||
{#if expanded.has(object._id)}
|
||||
<HierarchyElement {descendants} {model} level={level + 1} parent={object._id} />
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<style lang="scss">
|
||||
.firstCell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.menuRow {
|
||||
visibility: hidden;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
.expand {
|
||||
margin-left: -1.5rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
.expand,
|
||||
.menuRow {
|
||||
opacity: 0.6;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
td {
|
||||
padding: 0.5rem 1.5rem;
|
||||
text-align: left;
|
||||
&:first-child {
|
||||
padding-left: 2.5rem;
|
||||
}
|
||||
&:last-child {
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.tr-body {
|
||||
height: 3.25rem;
|
||||
color: var(--theme-caption-color);
|
||||
border-bottom: 1px solid var(--theme-button-border-hovered);
|
||||
&:hover .firstCell .menuRow {
|
||||
visibility: visible;
|
||||
}
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--theme-table-bg-hover);
|
||||
}
|
||||
}
|
||||
</style>
|
109
plugins/inventory-resources/src/components/HierarchyView.svelte
Normal file
109
plugins/inventory-resources/src/components/HierarchyView.svelte
Normal file
@ -0,0 +1,109 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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 { Class, Doc, DocumentQuery, FindOptions, Ref, SortingOrder } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Label, Loading } from '@anticrm/ui'
|
||||
import { buildModel } from '@anticrm/view-resources'
|
||||
import { Category } from '@anticrm/inventory'
|
||||
import HierarchyElement from './HierarchyElement.svelte'
|
||||
|
||||
export let _class: Ref<Class<Category>>
|
||||
export let query: DocumentQuery<Category> = {}
|
||||
export let options: FindOptions<Category> | undefined = undefined
|
||||
export let config: string[] = ['', 'modifiedOn']
|
||||
|
||||
let objects: Category[]
|
||||
let descendants: Map<Ref<Doc>, Category[]> = new Map<Ref<Doc>, Category[]>()
|
||||
|
||||
const q = createQuery()
|
||||
|
||||
async function update (_class: Ref<Class<Category>>, query: DocumentQuery<Category>, options?: FindOptions<Category>) {
|
||||
q.query(
|
||||
_class,
|
||||
query,
|
||||
(result) => {
|
||||
objects = result
|
||||
updateDescendants()
|
||||
},
|
||||
{ sort: { name: SortingOrder.Ascending }, ...options, limit: 200 }
|
||||
)
|
||||
}
|
||||
$: update(_class, query, options)
|
||||
|
||||
function updateDescendants (): void {
|
||||
descendants.clear()
|
||||
for (const doc of objects) {
|
||||
const current = descendants.get(doc.attachedTo)
|
||||
if (!current) {
|
||||
descendants.set(doc.attachedTo, [doc])
|
||||
} else {
|
||||
current.push(doc)
|
||||
descendants.set(doc.attachedTo, current)
|
||||
}
|
||||
}
|
||||
descendants = descendants
|
||||
}
|
||||
|
||||
const client = getClient()
|
||||
</script>
|
||||
|
||||
{#await buildModel({ client, _class, keys: config, options })}
|
||||
<Loading />
|
||||
{:then model}
|
||||
<table class="table-body">
|
||||
<thead>
|
||||
<tr class="tr-head">
|
||||
{#each model as attribute}
|
||||
<th>
|
||||
<div class="flex-row-center whitespace-nowrap">
|
||||
<Label label={attribute.label} />
|
||||
</div>
|
||||
</th>
|
||||
{/each}
|
||||
</tr>
|
||||
</thead>
|
||||
{#if objects}
|
||||
<tbody>
|
||||
<HierarchyElement {model} {descendants} />
|
||||
</tbody>
|
||||
{/if}
|
||||
</table>
|
||||
{/await}
|
||||
|
||||
<style lang="scss">
|
||||
.table-body {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
th {
|
||||
height: 2.5rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
color: var(--theme-content-dark-color);
|
||||
box-shadow: inset 0 -1px 0 0 var(--theme-bg-focused-color);
|
||||
user-select: none;
|
||||
z-index: 5;
|
||||
padding: 0.5rem 1.5rem;
|
||||
text-align: left;
|
||||
&:first-child {
|
||||
padding-left: 2.5rem;
|
||||
}
|
||||
&:last-child {
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,50 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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 { Product } from '@anticrm/inventory'
|
||||
import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
|
||||
import EditProduct from './EditProduct.svelte'
|
||||
import inventory from '../plugin'
|
||||
|
||||
export let value: Product
|
||||
|
||||
function show () {
|
||||
closeTooltip()
|
||||
showPopup(EditProduct, { _id: value._id }, 'full')
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<div class="sm-tool-icon container" on:click={show}>
|
||||
<Icon icon={inventory.icon.Products} size="medium" />
|
||||
<div class="overflow-label name">{value.name}</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
.name {
|
||||
margin-left: 0.5rem;
|
||||
font-weight: 500;
|
||||
text-align: left;
|
||||
color: var(--theme-content-accent-color);
|
||||
}
|
||||
&:hover .name {
|
||||
text-decoration: underline;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
}
|
||||
</style>
|
132
plugins/inventory-resources/src/components/Products.svelte
Normal file
132
plugins/inventory-resources/src/components/Products.svelte
Normal file
@ -0,0 +1,132 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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 { Button, EditWithIcon, Icon, IconSearch, Label, ScrollBox, showPopup } from '@anticrm/ui'
|
||||
import CreateProduct from './CreateProduct.svelte'
|
||||
import inventory from '../plugin'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import view, { Viewlet } from '@anticrm/view'
|
||||
|
||||
let search = ''
|
||||
$: resultQuery = search === '' ? {} : { $search: search }
|
||||
|
||||
const client = getClient()
|
||||
const tableDescriptor = client.findOne<Viewlet>(view.class.Viewlet, {
|
||||
attachTo: inventory.class.Product,
|
||||
descriptor: view.viewlet.Table
|
||||
})
|
||||
|
||||
function showCreateDialog (ev: Event) {
|
||||
showPopup(CreateProduct, { space: inventory.space.Products }, ev.target as HTMLElement)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="products-header-container">
|
||||
<div class="header-container">
|
||||
<div class="flex-row-center">
|
||||
<span class="icon"><Icon icon={inventory.icon.Products} size={'small'} /></span>
|
||||
<span class="label"><Label label={inventory.string.Products} /></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<EditWithIcon
|
||||
icon={IconSearch}
|
||||
placeholder={'Search'}
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
resultQuery = {}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label={inventory.string.CreateProductShort}
|
||||
primary={true}
|
||||
size={'small'}
|
||||
on:click={(ev) => showCreateDialog(ev)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="panel-component">
|
||||
<ScrollBox vertical stretch noShift>
|
||||
{#await tableDescriptor then descr}
|
||||
{#if descr}
|
||||
<Table
|
||||
_class={inventory.class.Product}
|
||||
config={descr.config}
|
||||
options={descr.options}
|
||||
query={resultQuery}
|
||||
enableChecking
|
||||
/>
|
||||
{/if}
|
||||
{/await}
|
||||
</ScrollBox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
padding-bottom: 1.25rem;
|
||||
|
||||
.panel-component {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-right: 1rem;
|
||||
height: 100%;
|
||||
border-radius: 1.25rem;
|
||||
background-color: var(--theme-bg-color);
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
.products-header-container {
|
||||
display: grid;
|
||||
grid-template-columns: auto;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: min-content;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
padding: 0 1.75rem 0 2.5rem;
|
||||
height: 4rem;
|
||||
min-height: 4rem;
|
||||
white-space: nowrap;
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
|
||||
.icon {
|
||||
margin-right: 0.5rem;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.label {
|
||||
flex-grow: 1;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
max-width: 35rem;
|
||||
}
|
||||
.label {
|
||||
font-weight: 500;
|
||||
font-size: 1rem;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,26 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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 { Variant } from '@anticrm/inventory'
|
||||
|
||||
export let value: Variant
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<div class="sm-tool-icon">
|
||||
{value.name}
|
||||
</div>
|
||||
{/if}
|
75
plugins/inventory-resources/src/components/Variants.svelte
Normal file
75
plugins/inventory-resources/src/components/Variants.svelte
Normal file
@ -0,0 +1,75 @@
|
||||
<!--
|
||||
// Copyright © 2020 Anticrm Platform Contributors.
|
||||
//
|
||||
// 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 type { Doc, Ref } from '@anticrm/core'
|
||||
import { Icon, CircleButton, IconAdd, Label, showPopup } from '@anticrm/ui'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import inventory from '../plugin'
|
||||
import CreateVariant from './CreateVariant.svelte'
|
||||
|
||||
export let objectId: Ref<Doc>
|
||||
|
||||
export let variants: number
|
||||
|
||||
const create = (ev: MouseEvent): void => showPopup(CreateVariant, { product: objectId }, ev.target as HTMLElement)
|
||||
</script>
|
||||
|
||||
<div class="variants-container">
|
||||
<div class="flex-row-center">
|
||||
<div class="title"><Label label={inventory.string.Variants} /></div>
|
||||
<CircleButton icon={IconAdd} size={'small'} selected on:click={create} />
|
||||
</div>
|
||||
{#if variants > 0}
|
||||
<Table
|
||||
_class={inventory.class.Variant}
|
||||
config={['', 'sku', 'modifiedOn']}
|
||||
options={{}}
|
||||
query={{ attachedTo: objectId }}
|
||||
loadingProps={{ length: variants }}
|
||||
/>
|
||||
{:else}
|
||||
<div class="flex-col-center mt-5 create-container">
|
||||
<Icon size={'large'} icon={inventory.icon.Variant} />
|
||||
<div class="small-text content-dark-color mt-2">
|
||||
<Label label={inventory.string.NoVariantsForProduct} />
|
||||
</div>
|
||||
<div class="small-text">
|
||||
<a href={'#'} on:click={create}><Label label={inventory.string.CreateVariant} /></a>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.variants-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
margin-right: 0.75rem;
|
||||
font-weight: 500;
|
||||
font-size: 1.25rem;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
}
|
||||
|
||||
.create-container {
|
||||
padding: 1rem;
|
||||
color: var(--theme-caption-color);
|
||||
background: var(--theme-bg-accent-color);
|
||||
border: 1px solid var(--theme-bg-accent-color);
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,8 @@
|
||||
<script lang="ts">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.5 8.25L6 3.75L10.5 8.25" stroke={fill} />
|
||||
</svg>
|
@ -0,0 +1,8 @@
|
||||
<script lang="ts">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" fill="none" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.5 3.75L6 8.25L1.5 3.75" stroke={fill} />
|
||||
</svg>
|
46
plugins/inventory-resources/src/index.ts
Normal file
46
plugins/inventory-resources/src/index.ts
Normal file
@ -0,0 +1,46 @@
|
||||
//
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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.
|
||||
//
|
||||
|
||||
import { Resources } from '@anticrm/platform'
|
||||
import Categories from './components/Categories.svelte'
|
||||
import CreateCategory from './components/CreateCategory.svelte'
|
||||
import CategoryPresenter from './components/CategoryPresenter.svelte'
|
||||
import Products from './components/Products.svelte'
|
||||
import ProductPresenter from './components/ProductPresenter.svelte'
|
||||
import EditProduct from './components/EditProduct.svelte'
|
||||
import Variants from './components/Variants.svelte'
|
||||
import VariantPresenter from './components/VariantPresenter.svelte'
|
||||
import { Doc } from '@anticrm/core'
|
||||
import { showPopup } from '@anticrm/ui'
|
||||
|
||||
async function createSubcategory (object: Doc): Promise<void> {
|
||||
showPopup(CreateCategory, { attachedTo: object._id })
|
||||
}
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
actionImpl: {
|
||||
CreateSubcategory: createSubcategory
|
||||
},
|
||||
component: {
|
||||
Categories,
|
||||
CategoryPresenter,
|
||||
Products,
|
||||
ProductPresenter,
|
||||
EditProduct,
|
||||
Variants,
|
||||
VariantPresenter
|
||||
}
|
||||
})
|
43
plugins/inventory-resources/src/plugin.ts
Normal file
43
plugins/inventory-resources/src/plugin.ts
Normal file
@ -0,0 +1,43 @@
|
||||
//
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { IntlString, mergeIds, StatusCode } from '@anticrm/platform'
|
||||
|
||||
import inventory, { inventoryId } from '@anticrm/inventory'
|
||||
|
||||
export default mergeIds(inventoryId, inventory, {
|
||||
status: {
|
||||
CategoryRequired: '' as StatusCode,
|
||||
NameRequired: '' as StatusCode
|
||||
},
|
||||
string: {
|
||||
Categories: '' as IntlString,
|
||||
Category: '' as IntlString,
|
||||
CreateCategoryShort: '' as IntlString,
|
||||
CreateCategory: '' as IntlString,
|
||||
CreateSubcategory: '' as IntlString,
|
||||
Inventory: '' as IntlString,
|
||||
CreateProductShort: '' as IntlString,
|
||||
CreateProduct: '' as IntlString,
|
||||
Products: '' as IntlString,
|
||||
Product: '' as IntlString,
|
||||
SKU: '' as IntlString,
|
||||
Variant: '' as IntlString,
|
||||
Variants: '' as IntlString,
|
||||
NoVariantsForProduct: '' as IntlString,
|
||||
CreateVariant: '' as IntlString
|
||||
}
|
||||
})
|
5
plugins/inventory-resources/svelte.config.js
Normal file
5
plugins/inventory-resources/svelte.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
const sveltePreprocess = require('svelte-preprocess')
|
||||
|
||||
module.exports = {
|
||||
preprocess: sveltePreprocess()
|
||||
};
|
15
plugins/inventory-resources/tsconfig.json
Normal file
15
plugins/inventory-resources/tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"declaration": true,
|
||||
"outDir": "./lib",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom"
|
||||
]
|
||||
}
|
||||
}
|
7
plugins/inventory/.eslintrc.js
Normal file
7
plugins/inventory/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
4
plugins/inventory/.npmignore
Normal file
4
plugins/inventory/.npmignore
Normal file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
32
plugins/inventory/package.json
Normal file
32
plugins/inventory/package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@anticrm/inventory",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"build:watch": "tsc",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"lint": "eslint src",
|
||||
"format": "prettier --write src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/platform-rig": "~0.6.0",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"prettier": "^2.4.1",
|
||||
"@rushstack/heft": "^0.41.1",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/core": "~0.6.11",
|
||||
"@anticrm/platform": "~0.6.5"
|
||||
}
|
||||
}
|
76
plugins/inventory/src/index.ts
Normal file
76
plugins/inventory/src/index.ts
Normal file
@ -0,0 +1,76 @@
|
||||
//
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 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.
|
||||
//
|
||||
|
||||
import { AttachedDoc, Class, Ref, Space } from '@anticrm/core'
|
||||
import type { Asset, Plugin } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Category extends AttachedDoc {
|
||||
name: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Product extends AttachedDoc {
|
||||
attachments?: number
|
||||
photos?: number
|
||||
variants?: number
|
||||
name: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Variant extends AttachedDoc {
|
||||
name: string
|
||||
sku: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const inventoryId = 'inventory' as Plugin
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
const inventory = plugin(inventoryId, {
|
||||
class: {
|
||||
Product: '' as Ref<Class<Product>>,
|
||||
Category: '' as Ref<Class<Category>>,
|
||||
Variant: '' as Ref<Class<Variant>>
|
||||
},
|
||||
icon: {
|
||||
InventoryApplication: '' as Asset,
|
||||
Products: '' as Asset,
|
||||
Categories: '' as Asset,
|
||||
Variant: '' as Asset
|
||||
},
|
||||
global: {
|
||||
// Global category root, if not attached to some other object.
|
||||
Category: '' as Ref<Category>
|
||||
},
|
||||
space: {
|
||||
Category: '' as Ref<Space>,
|
||||
Products: '' as Ref<Space>
|
||||
}
|
||||
})
|
||||
|
||||
export default inventory
|
9
plugins/inventory/tsconfig.json
Normal file
9
plugins/inventory/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./node_modules/@anticrm/platform-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"lib": ["esnext", "dom"]
|
||||
}
|
||||
}
|
20
rush.json
20
rush.json
@ -921,5 +921,25 @@
|
||||
"projectFolder": "plugins/gmail-resources",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/model-inventory",
|
||||
"projectFolder": "models/inventory",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/inventory",
|
||||
"projectFolder": "plugins/inventory",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/inventory-assets",
|
||||
"projectFolder": "plugins/inventory-assets",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/inventory-resources",
|
||||
"projectFolder": "plugins/inventory-resources",
|
||||
"shouldPublish": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user