Merge branch 'develop' into staging

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev
2025-01-31 12:41:17 +07:00
162 changed files with 4118 additions and 2549 deletions
+2
View File
@@ -107,3 +107,5 @@ dump
**/logs/**
dev/tool/history.json
.aider*
/combined_dependencies
.tmp
+5 -4
View File
@@ -37,10 +37,11 @@
// "FULLTEXT_URL": "http://localhost:4700",
"FULLTEXT_URL": "http://host.docker.internal:4702",
// "MONGO_URL": "mongodb://localhost:27017",
"DB_URL": "mongodb://localhost:27017",
// "DB_URL": "mongodb://localhost:27017",
// "DB_URL": "postgresql://postgres:example@localhost:5432",
// "DB_URL": "postgresql://root@host.docker.internal:26257/defaultdb?sslmode=disable",
"SERVER_PORT": "3333",
"DB_URL": "postgresql://root@host.docker.internal:26257/defaultdb?sslmode=disable",
"GREEN_URL": "http://host.docker.internal:6767?token=secret",
"SERVER_PORT": "3332",
"APM_SERVER_URL2": "http://localhost:8200",
"METRICS_CONSOLE": "false",
"METRICS_FILE": "${workspaceRoot}/metrics.txt", // Show metrics in console evert 30 seconds.,
@@ -394,7 +395,7 @@
"DB_URL": "mongodb://localhost:27017",
"MODEL_JSON": "${workspaceRoot}/models/all/bundle/model.json",
"SECRET": "secret",
"REGION": "pg",
"REGION": "cockroach",
"BUCKET_NAME":"backups",
"INTERVAL":"30"
},
+1 -1
View File
@@ -233,5 +233,5 @@ node ./common/scripts/bump.js -p projectName
This project is tested with BrowserStack.
<sub><sup>&copy; 2024 <a href="https://hardcoreeng.com">Hardcore Engineering Inc</a>.</sup></sub>
<sub><sup>&copy; 2025 <a href="https://hardcoreeng.com">Hardcore Engineering Inc</a>.</sup></sub>
+1 -1
View File
@@ -238,7 +238,7 @@
"summary": "Build docker with platform",
"description": "use to build all docker containers required for platform",
"safeForSimultaneousRushProcesses": true,
"shellCommand": "rush docker:build -p 20 --to @hcengineering/pod-server --to @hcengineering/pod-front --to @hcengineering/prod --to @hcengineering/pod-account --to @hcengineering/pod-workspace --to @hcengineering/pod-collaborator --to @hcengineering/tool --to @hcengineering/pod-print --to @hcengineering/pod-sign --to @hcengineering/pod-analytics-collector --to @hcengineering/rekoni-service --to @hcengineering/pod-ai-bot --to @hcengineering/import-tool --to @hcengineering/pod-stats --to @hcengineering/pod-fulltext --to @hcengineering/pod-love"
"shellCommand": "rush docker:build -p 20 --to @hcengineering/pod-server --to @hcengineering/pod-front --to @hcengineering/prod --to @hcengineering/pod-account --to @hcengineering/pod-workspace --to @hcengineering/pod-collaborator --to @hcengineering/tool --to @hcengineering/pod-print --to @hcengineering/pod-sign --to @hcengineering/pod-analytics-collector --to @hcengineering/rekoni-service --to @hcengineering/pod-ai-bot --to @hcengineering/import-tool --to @hcengineering/pod-stats --to @hcengineering/pod-fulltext --to @hcengineering/pod-love --to @hcengineering/green"
},
{
"commandKind": "global",
+2074 -1960
View File
File diff suppressed because it is too large Load Diff
+82
View File
@@ -0,0 +1,82 @@
#!/bin/bash
# Get the absolute path for the base directory
BASE_DIR=$(pwd)
# Create a directory to store the combined dependencies
DEPS_DIR="$BASE_DIR/combined_dependencies"
mkdir -p "$DEPS_DIR"
# Create temporary package.json files
EXTERNAL_PACKAGE="$DEPS_DIR/external_package.json"
COMBINED_PACKAGE="$DEPS_DIR/combined_package.json"
# Create initial combined package.json
echo '{"name": "combined-dependencies", "dependencies": {}}' > "$COMBINED_PACKAGE"
# Find all package.json files recursively, excluding node_modules and focusing on workspace packages
echo "Finding workspace package.json files..."
find . -name "package.json" -not -path "*/node_modules/*" -type f | while read -r file; do
if grep -q '"name": "@hcengineering/' "$file"; then
echo "$file"
fi
done > "$DEPS_DIR/package_list.txt"
# Process each package.json and combine dependencies
echo "Combining dependencies from workspace packages..."
while IFS= read -r package_file; do
echo "Processing: $package_file"
# Extract dependencies and merge them into combined package.json
deps=$(jq -r '.dependencies // {}' "$package_file")
jq -s '.[0].dependencies *= .[1] | .[0]' "$COMBINED_PACKAGE" <(echo "$deps") > "$COMBINED_PACKAGE.tmp"
mv "$COMBINED_PACKAGE.tmp" "$COMBINED_PACKAGE"
done < "$DEPS_DIR/package_list.txt"
# Create filtered external package.json excluding @hcengineering packages
jq '{"name": "external-dependencies", "dependencies": (.dependencies | with_entries(select(.key | startswith("@hcengineering/") | not)))}' "$COMBINED_PACKAGE" > "$EXTERNAL_PACKAGE"
# Create a temporary directory for checking outdated packages
TEMP_DIR="$DEPS_DIR/temp"
mkdir -p "$TEMP_DIR"
cp "$EXTERNAL_PACKAGE" "$TEMP_DIR/package.json"
# Check outdated packages
echo "Checking for outdated packages..."
cd "$TEMP_DIR"
npm install --force --package-lock-only > /dev/null 2>&1
npm outdated --json > "../outdated.json" 2>/dev/null
cd - > /dev/null
# Generate report
echo "Generating report..."
echo "Project Dependencies Analysis" > "$DEPS_DIR/dependencies_report.txt"
echo "Generated on: $(date)" >> "$DEPS_DIR/dependencies_report.txt"
echo "----------------------------------------" >> "$DEPS_DIR/dependencies_report.txt"
echo -e "\nOutdated External Dependencies:" >> "$DEPS_DIR/dependencies_report.txt"
echo "Package Version Latest" >> "$DEPS_DIR/dependencies_report.txt"
echo "----------------------------------------" >> "$DEPS_DIR/dependencies_report.txt"
# Extract current versions from package.json
CURRENT_VERSIONS=$(jq -r '.dependencies | to_entries[] | "\(.key)|\(.value)"' "$EXTERNAL_PACKAGE")
# Format and append outdated packages to report
if [ -s "$DEPS_DIR/outdated.json" ]; then
echo "$CURRENT_VERSIONS" | while IFS='|' read -r package version; do
latest=$(jq -r --arg pkg "$package" '.[$pkg].latest // empty' "$DEPS_DIR/outdated.json")
if [ ! -z "$latest" ]; then
version=$(echo "$version" | sed 's/[\^~]//g')
if [ "$version" != "$latest" ]; then
printf "%-35s %-10s %-10s\n" "$package" "$version" "$latest" >> "$DEPS_DIR/dependencies_report.txt"
fi
fi
done
fi
# Cleanup
rm -rf "$TEMP_DIR"
# Create summary
echo -e "\nSummary:"
echo "----------------------------------------"
echo "Report generated in: $DEPS_DIR/dependencies_report.txt"
echo "Total workspace packages processed: $(wc -l < "$DEPS_DIR/package_list.txt")"
+1 -1
View File
@@ -224,7 +224,7 @@
"electron-store": "^8.2.0",
"electron-log": "^5.1.7",
"electron-updater": "^6.3.4",
"livekit-client": "^2.7.5",
"livekit-client": "^2.8.1",
"@hcengineering/server-backup": "^0.6.0",
"ws": "^8.18.0"
},
+4 -2
View File
@@ -23,7 +23,9 @@ async function loadServerConfig (url: string): Promise<any> {
do {
try {
res = await fetch(url)
res = await fetch(url, {
keepalive: true
})
break
} catch (e) {
retries--
@@ -93,7 +95,7 @@ const expose: IPCMainExposed = {
branding: async () => {
const cfg = await expose.config()
const branding: BrandingMap = await (
await fetch(cfg.BRANDING_URL ?? concatLink(cfg.FRONT_URL, 'branding.json'))
await fetch(cfg.BRANDING_URL ?? concatLink(cfg.FRONT_URL, 'branding.json'), { keepalive: true })
).json()
const host = await ipcRenderer.invoke('get-host')
return branding[host] ?? {}
+15
View File
@@ -357,6 +357,21 @@ services:
- LAST_NAME_FIRST=true
- BRANDING_PATH=/var/cfg/branding.json
restart: unless-stopped
green:
image: hardcoreeng/green
extra_hosts:
- 'host.docker.internal:host-gateway'
links:
- cockroach
- stats
ports:
- 6767:6767
environment:
- PORT=6767
- AUTH_TOKEN=secret
- STATS_URL=http://host.docker.internal:4900
- DB_URL=postgresql://root@host.docker.internal:26257/defaultdb?sslmode=disable
restart: unless-stopped
rekoni:
image: hardcoreeng/rekoni-service
restart: unless-stopped
+1 -1
View File
@@ -246,7 +246,7 @@
"@hcengineering/card": "^0.6.0",
"@hcengineering/card-assets": "^0.6.0",
"@hcengineering/card-resources": "^0.6.0",
"@sentry/svelte": "~7.101.0",
"@sentry/svelte": "^8.52.1",
"posthog-js": "~1.122.0"
}
}
+1 -1
View File
@@ -48,5 +48,5 @@
"UPLOAD_CONFIG": "",
"UPLOAD_URL": "https://dl.hc.engineering/upload/form-data/:workspace",
"TRANSACTOR_OVERRIDE": "wss://transactor00.hc.engineering"
"TRANSACTOR_OVERRIDE": "ws://localhost:3335"
}
+89 -29
View File
@@ -153,20 +153,20 @@ export interface Config {
PRINT_URL?: string
POSTHOG_API_KEY?: string
POSTHOG_HOST?: string
ANALYTICS_COLLECTOR_URL?:string
ANALYTICS_COLLECTOR_URL?: string
BRANDING_URL?: string
TELEGRAM_BOT_URL?: string
AI_URL?:string
AI_URL?: string
DISABLE_SIGNUP?: string
LINK_PREVIEW_URL?: string
PASSWORD_STRICTNESS?: "very_strict" | "strict" | "normal" | "none"
PASSWORD_STRICTNESS?: 'very_strict' | 'strict' | 'normal' | 'none'
// Could be defined for dev environment
FRONT_URL?: string
PREVIEW_CONFIG?: string
UPLOAD_CONFIG?: string
STATS_URL?: string
PRESENCE_URL?: string
USE_BINARY_PROTOCOL?: boolean,
USE_BINARY_PROTOCOL?: boolean
TRANSACTOR_OVERRIDE?: string
BACKUP_URL?: string
}
@@ -191,16 +191,16 @@ export interface Branding {
export type BrandingMap = Record<string, Branding>
const clientType = process.env.CLIENT_TYPE
const configs: Record<string, string> = {
const configs: Record<string, string> = {
'dev-production': '/config-dev.json',
'dev-huly': '/config-huly.json',
'dev-bold': '/config.json',
'dev-server': '/config.json',
'dev-worker': '/config-worker.json',
'dev-worker-local': '/config-worker-local.json',
'dev-worker-local': '/config-worker-local.json'
}
const PASSWORD_REQUIREMENTS : Record<Config['PASSWORD_STRICTNESS'], Record<string, number>> = {
const PASSWORD_REQUIREMENTS: Record<Config['PASSWORD_STRICTNESS'], Record<string, number>> = {
very_strict: {
MinDigits: 4,
MinLength: 32,
@@ -235,12 +235,21 @@ function configureI18n(): void {
//Add localization
addStringsLoader(platformId, async (lang: string) => await import(`@hcengineering/platform/lang/${lang}.json`))
addStringsLoader(coreId, async (lang: string) => await import(`@hcengineering/core/lang/${lang}.json`))
addStringsLoader(presentationId, async (lang: string) => await import(`@hcengineering/presentation/lang/${lang}.json`))
addStringsLoader(textEditorId, async (lang: string) => await import(`@hcengineering/text-editor-assets/lang/${lang}.json`))
addStringsLoader(
presentationId,
async (lang: string) => await import(`@hcengineering/presentation/lang/${lang}.json`)
)
addStringsLoader(
textEditorId,
async (lang: string) => await import(`@hcengineering/text-editor-assets/lang/${lang}.json`)
)
addStringsLoader(uiId, async (lang: string) => await import(`@hcengineering/ui/lang/${lang}.json`))
addStringsLoader(uploaderId, async (lang: string) => await import(`@hcengineering/uploader-assets/lang/${lang}.json`))
addStringsLoader(activityId, async (lang: string) => await import(`@hcengineering/activity-assets/lang/${lang}.json`))
addStringsLoader(attachmentId, async (lang: string) => await import(`@hcengineering/attachment-assets/lang/${lang}.json`))
addStringsLoader(
attachmentId,
async (lang: string) => await import(`@hcengineering/attachment-assets/lang/${lang}.json`)
)
addStringsLoader(bitrixId, async (lang: string) => await import(`@hcengineering/bitrix-assets/lang/${lang}.json`))
addStringsLoader(boardId, async (lang: string) => await import(`@hcengineering/board-assets/lang/${lang}.json`))
addStringsLoader(calendarId, async (lang: string) => await import(`@hcengineering/calendar-assets/lang/${lang}.json`))
@@ -249,12 +258,21 @@ function configureI18n(): void {
addStringsLoader(driveId, async (lang: string) => await import(`@hcengineering/drive-assets/lang/${lang}.json`))
addStringsLoader(gmailId, async (lang: string) => await import(`@hcengineering/gmail-assets/lang/${lang}.json`))
addStringsLoader(hrId, async (lang: string) => await import(`@hcengineering/hr-assets/lang/${lang}.json`))
addStringsLoader(inventoryId, async (lang: string) => await import(`@hcengineering/inventory-assets/lang/${lang}.json`))
addStringsLoader(
inventoryId,
async (lang: string) => await import(`@hcengineering/inventory-assets/lang/${lang}.json`)
)
addStringsLoader(leadId, async (lang: string) => await import(`@hcengineering/lead-assets/lang/${lang}.json`))
addStringsLoader(loginId, async (lang: string) => await import(`@hcengineering/login-assets/lang/${lang}.json`))
addStringsLoader(notificationId, async (lang: string) => await import(`@hcengineering/notification-assets/lang/${lang}.json`))
addStringsLoader(
notificationId,
async (lang: string) => await import(`@hcengineering/notification-assets/lang/${lang}.json`)
)
addStringsLoader(onboardId, async (lang: string) => await import(`@hcengineering/onboard-assets/lang/${lang}.json`))
addStringsLoader(preferenceId, async (lang: string) => await import(`@hcengineering/preference-assets/lang/${lang}.json`))
addStringsLoader(
preferenceId,
async (lang: string) => await import(`@hcengineering/preference-assets/lang/${lang}.json`)
)
addStringsLoader(recruitId, async (lang: string) => await import(`@hcengineering/recruit-assets/lang/${lang}.json`))
addStringsLoader(requestId, async (lang: string) => await import(`@hcengineering/request-assets/lang/${lang}.json`))
addStringsLoader(settingId, async (lang: string) => await import(`@hcengineering/setting-assets/lang/${lang}.json`))
@@ -262,25 +280,46 @@ function configureI18n(): void {
addStringsLoader(tagsId, async (lang: string) => await import(`@hcengineering/tags-assets/lang/${lang}.json`))
addStringsLoader(taskId, async (lang: string) => await import(`@hcengineering/task-assets/lang/${lang}.json`))
addStringsLoader(telegramId, async (lang: string) => await import(`@hcengineering/telegram-assets/lang/${lang}.json`))
addStringsLoader(templatesId, async (lang: string) => await import(`@hcengineering/templates-assets/lang/${lang}.json`))
addStringsLoader(
templatesId,
async (lang: string) => await import(`@hcengineering/templates-assets/lang/${lang}.json`)
)
addStringsLoader(trackerId, async (lang: string) => await import(`@hcengineering/tracker-assets/lang/${lang}.json`))
addStringsLoader(viewId, async (lang: string) => await import(`@hcengineering/view-assets/lang/${lang}.json`))
addStringsLoader(workbenchId, async (lang: string) => await import(`@hcengineering/workbench-assets/lang/${lang}.json`))
addStringsLoader(
workbenchId,
async (lang: string) => await import(`@hcengineering/workbench-assets/lang/${lang}.json`)
)
addStringsLoader(desktopPreferencesId, async (lang: string) => await import(`@hcengineering/desktop-preferences-assets/lang/${lang}.json`))
addStringsLoader(
desktopPreferencesId,
async (lang: string) => await import(`@hcengineering/desktop-preferences-assets/lang/${lang}.json`)
)
addStringsLoader(diffviewId, async (lang: string) => await import(`@hcengineering/diffview-assets/lang/${lang}.json`))
addStringsLoader(documentId, async (lang: string) => await import(`@hcengineering/document-assets/lang/${lang}.json`))
addStringsLoader(timeId, async (lang: string) => await import(`@hcengineering/time-assets/lang/${lang}.json`))
addStringsLoader(githubId, async (lang: string) => await import(`@hcengineering/github-assets/lang/${lang}.json`))
addStringsLoader(documentsId, async (lang: string) => await import(`@hcengineering/controlled-documents-assets/lang/${lang}.json`))
addStringsLoader(
documentsId,
async (lang: string) => await import(`@hcengineering/controlled-documents-assets/lang/${lang}.json`)
)
addStringsLoader(productsId, async (lang: string) => await import(`@hcengineering/products-assets/lang/${lang}.json`))
addStringsLoader(questionsId, async (lang: string) => await import(`@hcengineering/questions-assets/lang/${lang}.json`))
addStringsLoader(
questionsId,
async (lang: string) => await import(`@hcengineering/questions-assets/lang/${lang}.json`)
)
addStringsLoader(trainingId, async (lang: string) => await import(`@hcengineering/training-assets/lang/${lang}.json`))
addStringsLoader(guestId, async (lang: string) => await import(`@hcengineering/guest-assets/lang/${lang}.json`))
addStringsLoader(loveId, async (lang: string) => await import(`@hcengineering/love-assets/lang/${lang}.json`))
addStringsLoader(printId, async (lang: string) => await import(`@hcengineering/print-assets/lang/${lang}.json`))
addStringsLoader(analyticsCollectorId, async (lang: string) => await import(`@hcengineering/analytics-collector-assets/lang/${lang}.json`))
addStringsLoader(testManagementId, async (lang: string) => await import(`@hcengineering/test-management-assets/lang/${lang}.json`))
addStringsLoader(
analyticsCollectorId,
async (lang: string) => await import(`@hcengineering/analytics-collector-assets/lang/${lang}.json`)
)
addStringsLoader(
testManagementId,
async (lang: string) => await import(`@hcengineering/test-management-assets/lang/${lang}.json`)
)
addStringsLoader(surveyId, async (lang: string) => await import(`@hcengineering/survey-assets/lang/${lang}.json`))
addStringsLoader(cardId, async (lang: string) => await import(`@hcengineering/card-assets/lang/${lang}.json`))
}
@@ -301,7 +340,8 @@ export async function configurePlatform() {
configureI18n()
const config: Config = await loadServerConfig(configs[clientType] ?? '/config.json')
const branding: BrandingMap = config.BRANDING_URL !== undefined ? await (await fetch(config.BRANDING_URL)).json() : {}
const branding: BrandingMap =
config.BRANDING_URL !== undefined ? await (await fetch(config.BRANDING_URL, { keepalive: true })).json() : {}
const myBranding = branding[window.location.host] ?? {}
console.log('loading configuration', config)
@@ -342,7 +382,7 @@ export async function configurePlatform() {
setMetadata(login.metadata.DisableSignUp, config.DISABLE_SIGNUP === 'true')
setMetadata(login.metadata.PasswordValidations, PASSWORD_REQUIREMENTS[config.PASSWORD_STRICTNESS ?? 'none'])
setMetadata(presentation.metadata.FilesURL, config.FILES_URL)
setMetadata(presentation.metadata.UploadURL, config.UPLOAD_URL)
setMetadata(presentation.metadata.CollaboratorUrl, config.COLLABORATOR_URL)
@@ -384,7 +424,9 @@ export async function configurePlatform() {
setMetadata(sign.metadata.SignURL, config.SIGN_URL)
setMetadata(presence.metadata.PresenceUrl, config.PRESENCE_URL ?? '')
const languages = myBranding.languages ? (myBranding.languages as string).split(',').map((l) => l.trim()) : ['en', 'ru', 'es', 'pt', 'zh', 'fr', 'cs', 'it', 'de']
const languages = myBranding.languages
? (myBranding.languages as string).split(',').map((l) => l.trim())
: ['en', 'ru', 'es', 'pt', 'zh', 'fr', 'cs', 'it', 'de']
setMetadata(uiPlugin.metadata.Languages, languages)
@@ -418,15 +460,24 @@ export async function configurePlatform() {
addLocation(telegramId, () => import(/* webpackChunkName: "telegram" */ '@hcengineering/telegram-resources'))
addLocation(attachmentId, () => import(/* webpackChunkName: "attachment" */ '@hcengineering/attachment-resources'))
addLocation(gmailId, () => import(/* webpackChunkName: "gmail" */ '@hcengineering/gmail-resources'))
addLocation(imageCropperId, () => import(/* webpackChunkName: "image-cropper" */ '@hcengineering/image-cropper-resources'))
addLocation(
imageCropperId,
() => import(/* webpackChunkName: "image-cropper" */ '@hcengineering/image-cropper-resources')
)
addLocation(inventoryId, () => import(/* webpackChunkName: "inventory" */ '@hcengineering/inventory-resources'))
addLocation(templatesId, () => import(/* webpackChunkName: "templates" */ '@hcengineering/templates-resources'))
addLocation(notificationId, () => import(/* webpackChunkName: "notification" */ '@hcengineering/notification-resources'))
addLocation(
notificationId,
() => import(/* webpackChunkName: "notification" */ '@hcengineering/notification-resources')
)
addLocation(tagsId, () => import(/* webpackChunkName: "tags" */ '@hcengineering/tags-resources'))
addLocation(calendarId, () => import(/* webpackChunkName: "calendar" */ '@hcengineering/calendar-resources'))
addLocation(diffviewId, () => import(/* webpackChunkName: "diffview" */ '@hcengineering/diffview-resources'))
addLocation(timeId, () => import(/* webpackChunkName: "time" */ '@hcengineering/time-resources'))
addLocation(desktopPreferencesId, () => import(/* webpackChunkName: "desktop-preferences" */ '@hcengineering/desktop-preferences-resources'))
addLocation(
desktopPreferencesId,
() => import(/* webpackChunkName: "desktop-preferences" */ '@hcengineering/desktop-preferences-resources')
)
addLocation(analyticsCollectorId, async () => await import('@hcengineering/analytics-collector-resources'))
addLocation(aiBotId, async () => await import('@hcengineering/ai-bot-resources'))
@@ -443,13 +494,19 @@ export async function configurePlatform() {
addLocation(questionsId, () => import(/* webpackChunkName: "training" */ '@hcengineering/questions-resources'))
addLocation(trainingId, () => import(/* webpackChunkName: "training" */ '@hcengineering/training-resources'))
addLocation(productsId, () => import(/* webpackChunkName: "products" */ '@hcengineering/products-resources'))
addLocation(documentsId, () => import(/* webpackChunkName: "documents" */ '@hcengineering/controlled-documents-resources'))
addLocation(
documentsId,
() => import(/* webpackChunkName: "documents" */ '@hcengineering/controlled-documents-resources')
)
addLocation(guestId, () => import(/* webpackChunkName: "guest" */ '@hcengineering/guest-resources'))
addLocation(loveId, () => import(/* webpackChunkName: "love" */ '@hcengineering/love-resources'))
addLocation(printId, () => import(/* webpackChunkName: "print" */ '@hcengineering/print-resources'))
addLocation(textEditorId, () => import(/* webpackChunkName: "text-editor" */ '@hcengineering/text-editor-resources'))
addLocation(uploaderId, () => import(/* webpackChunkName: "uploader" */ '@hcengineering/uploader-resources'))
addLocation(testManagementId, () => import(/* webpackChunkName: "test-management" */ '@hcengineering/test-management-resources'))
addLocation(
testManagementId,
() => import(/* webpackChunkName: "test-management" */ '@hcengineering/test-management-resources')
)
addLocation(surveyId, () => import(/* webpackChunkName: "survey" */ '@hcengineering/survey-resources'))
addLocation(presenceId, () => import(/* webpackChunkName: "presence" */ '@hcengineering/presence-resources'))
addLocation(cardId, () => import(/* webpackChunkName: "card" */ '@hcengineering/card-resources'))
@@ -460,7 +517,10 @@ export async function configurePlatform() {
// Use binary response transfer for faster performance and small transfer sizes.
const binaryOverride = localStorage.getItem(client.metadata.UseBinaryProtocol)
setMetadata(client.metadata.UseBinaryProtocol, binaryOverride != null ? binaryOverride === 'true' : (config.USE_BINARY_PROTOCOL ?? true))
setMetadata(
client.metadata.UseBinaryProtocol,
binaryOverride != null ? binaryOverride === 'true' : config.USE_BINARY_PROTOCOL ?? true
)
// Disable for now, since it causes performance issues on linux/docker/kubernetes boxes for now.
setMetadata(client.metadata.UseProtocolCompression, true)
+1 -1
View File
@@ -160,7 +160,7 @@
"commander": "^8.1.0",
"csv-parse": "~5.1.0",
"email-addresses": "^5.0.0",
"fast-equals": "^5.0.1",
"fast-equals": "^5.2.2",
"libphonenumber-js": "^1.9.46",
"mime-types": "~2.1.34",
"mongodb": "^6.12.0",
+2 -1
View File
@@ -123,6 +123,7 @@ import type { PipelineFactory, StorageAdapter, StorageAdapterEx } from '@hcengin
import { deepEqual } from 'fast-equals'
import { createWriteStream, readFileSync } from 'fs'
import { getAccountDBUrl, getMongoDBUrl } from './__start'
import { fillGithubUsers, fixAccountEmails, renameAccount } from './account'
import {
benchmark,
benchmarkWorker,
@@ -154,7 +155,6 @@ import {
import { reindexWorkspace } from './fulltext'
import { restoreControlledDocContentMongo, restoreMarkupRefsMongo, restoreWikiContentMongo } from './markup'
import { fixMixinForeignAttributes, showMixinForeignAttributes } from './mixin'
import { fixAccountEmails, renameAccount, fillGithubUsers } from './account'
import { copyToDatalake, moveFiles, showLostFiles } from './storage'
const colorConstants = {
@@ -200,6 +200,7 @@ export function devTool (
registerTxAdapterFactory('postgresql', createPostgresTxAdapter, true)
registerAdapterFactory('postgresql', createPostgresAdapter, true)
registerDestroyFactory('postgresql', createPostgreeDestroyAdapter, true)
registerServerPlugins()
registerStringLoaders()
+27 -4
View File
@@ -150,6 +150,7 @@ export function createModel (builder: Builder): void {
['inProgress', documents.string.InProgress, {}],
['effective', documents.string.Effective, {}],
['archived', documents.string.Archived, {}],
['obsolete', documents.string.Obsolete, {}],
['all', documents.string.All, {}]
]
}
@@ -170,6 +171,7 @@ export function createModel (builder: Builder): void {
['effective', documents.string.Effective, {}],
['inProgress', documents.string.InProgress, {}],
['archived', documents.string.Archived, {}],
['obsolete', documents.string.Obsolete, {}],
['all', documents.string.All, {}]
]
}
@@ -774,16 +776,37 @@ export function createModel (builder: Builder): void {
documents.action.DeleteDocument
)
// createAction(
// builder,
// {
// action: documents.actionImpl.ArchiveDocument,
// label: view.string.Archive,
// icon: view.icon.Archive,
// input: 'any',
// category: view.category.General,
// target: documents.class.Document,
// visibilityTester: documents.function.CanArchiveDocument,
// query: {
// state: DocumentState.Effective
// },
// context: {
// mode: ['context', 'browser'],
// group: 'remove'
// }
// },
// documents.action.ArchiveDocument
// )
createAction(
builder,
{
action: documents.actionImpl.ArchiveDocument,
label: view.string.Archive,
action: documents.actionImpl.MakeDocumentObsolete,
label: documents.string.MakeDocumentObsolete,
icon: view.icon.Archive,
input: 'any',
category: view.category.General,
target: documents.class.Document,
visibilityTester: documents.function.CanArchiveDocument,
visibilityTester: documents.function.CanMakeDocumentObsolete,
query: {
state: DocumentState.Effective
},
@@ -792,7 +815,7 @@ export function createModel (builder: Builder): void {
group: 'remove'
}
},
documents.action.ArchiveDocument
documents.action.MakeDocumentObsolete
)
createAction(
@@ -68,6 +68,7 @@ export default mergeIds(documentsId, documents, {
TransferTemplate: '' as ViewAction,
DeleteDocument: '' as ViewAction,
ArchiveDocument: '' as ViewAction,
MakeDocumentObsolete: '' as ViewAction,
TransferDocument: '' as ViewAction,
EditDocSpace: '' as ViewAction
},
+1 -1
View File
@@ -43,7 +43,7 @@
"@hcengineering/analytics": "^0.6.0",
"winston": "^3.11.0",
"winston-daily-rotate-file": "^5.0.0",
"@sentry/node": "^8.48.0"
"@sentry/node": "^8.52.1"
},
"repository": "https://github.com/hcengineering/platform",
"publishConfig": {
+1 -1
View File
@@ -24,7 +24,7 @@ export interface ServerConfig {
export async function loadServerConfig (url: string): Promise<ServerConfig> {
const configUrl = concatLink(url, '/config.json')
const res = await fetch(configUrl)
const res = await fetch(configUrl, { keepalive: true })
if (res.ok) {
return (await res.json()) as ServerConfig
}
+1 -1
View File
@@ -40,7 +40,7 @@
"dependencies": {
"@hcengineering/platform": "^0.6.11",
"@hcengineering/analytics": "^0.6.0",
"fast-equals": "^5.0.1"
"fast-equals": "^5.2.2"
},
"repository": "https://github.com/hcengineering/platform",
"publishConfig": {
+69
View File
@@ -191,6 +191,75 @@ export class MeasureMetricsContext implements MeasureContext {
}
}
export class NoMetricsContext implements MeasureContext {
logger: MeasureLogger
id?: string
contextData: object = {}
constructor (logger?: MeasureLogger) {
this.logger = logger ?? consoleLogger({})
}
measure (name: string, value: number, override?: boolean): void {}
newChild (
name: string,
params: ParamsType,
fullParams?: FullParamsType | (() => FullParamsType),
logger?: MeasureLogger
): MeasureContext {
const result = new NoMetricsContext(logger ?? this.logger)
result.id = this.id
result.contextData = this.contextData
return result
}
with<T>(
name: string,
params: ParamsType,
op: (ctx: MeasureContext) => T | Promise<T>,
fullParams?: ParamsType | (() => FullParamsType)
): Promise<T> {
const r = op(this.newChild(name, params, fullParams, this.logger))
return r instanceof Promise ? r : Promise.resolve(r)
}
withSync<T>(
name: string,
params: ParamsType,
op: (ctx: MeasureContext) => T,
fullParams?: ParamsType | (() => FullParamsType)
): T {
const c = this.newChild(name, params, fullParams, this.logger)
return op(c)
}
withLog<T>(
name: string,
params: ParamsType,
op: (ctx: MeasureContext) => T | Promise<T>,
fullParams?: ParamsType
): Promise<T> {
const r = op(this.newChild(name, params, fullParams, this.logger))
return r instanceof Promise ? r : Promise.resolve(r)
}
error (message: string, args?: Record<string, any>): void {
this.logger.error(message, { ...args })
}
info (message: string, args?: Record<string, any>): void {
this.logger.info(message, { ...args })
}
warn (message: string, args?: Record<string, any>): void {
this.logger.warn(message, { ...args })
}
end (): void {}
}
/**
* Allow to use decorator for context enabled functions
*/
+2 -2
View File
@@ -40,7 +40,7 @@
"dependencies": {
"svelte": "^4.2.19",
"@hcengineering/ui": "^0.6.15",
"highlight.js": "~11.8.0",
"lowlight": "^3.1.0"
"highlight.js": "^11.11.1",
"lowlight": "^3.3.0"
}
}
+1 -1
View File
@@ -44,6 +44,6 @@
"@hcengineering/analytics": "^0.6.0",
"@hcengineering/rank": "^0.6.4",
"toposort": "^2.0.2",
"fast-equals": "^5.0.1"
"fast-equals": "^5.2.2"
}
}
+1 -1
View File
@@ -53,7 +53,7 @@
"svelte": "^4.2.19",
"@hcengineering/client": "^0.6.18",
"@hcengineering/collaborator-client": "^0.6.4",
"fast-equals": "^5.0.1",
"fast-equals": "^5.2.2",
"png-chunks-extract": "^1.0.0",
"uuid": "^8.3.2"
},
+1 -1
View File
@@ -782,7 +782,7 @@ export async function loadServerConfig (url: string): Promise<any> {
do {
try {
res = await fetch(url)
res = await fetch(url, { keepalive: true })
break
} catch (e: any) {
retries--
+1 -1
View File
@@ -41,7 +41,7 @@
"@hcengineering/platform": "^0.6.11",
"@hcengineering/core": "^0.6.32",
"@hcengineering/analytics": "^0.6.0",
"fast-equals": "^5.0.1"
"fast-equals": "^5.2.2"
},
"repository": "https://github.com/hcengineering/platform",
"publishConfig": {
+1 -1
View File
@@ -37,6 +37,6 @@
"dependencies": {
"@hcengineering/platform": "^0.6.11",
"@hcengineering/core": "^0.6.32",
"fast-equals": "^5.0.1"
"fast-equals": "^5.2.2"
}
}
+1 -1
View File
@@ -40,7 +40,7 @@
},
"dependencies": {
"@hcengineering/core": "^0.6.32",
"fast-equals": "^5.0.1"
"fast-equals": "^5.2.2"
},
"repository": "https://github.com/hcengineering/platform",
"publishConfig": {
+5 -5
View File
@@ -41,11 +41,11 @@
"dependencies": {
"@hcengineering/core": "^0.6.32",
"@hcengineering/text": "^0.6.5",
"@tiptap/core": "^2.6.6",
"@tiptap/pm": "^2.6.6",
"fast-equals": "^5.0.1",
"yjs": "^13.6.19",
"y-prosemirror": "^1.2.12"
"@tiptap/core": "^2.11.3",
"@tiptap/pm": "^2.11.3",
"fast-equals": "^5.2.2",
"yjs": "^13.6.23",
"y-prosemirror": "^1.2.15"
},
"repository": "https://github.com/hcengineering/platform",
"publishConfig": {
+22 -22
View File
@@ -41,30 +41,30 @@
"dependencies": {
"@hcengineering/core": "^0.6.32",
"@hcengineering/text-core": "^0.6.0",
"@tiptap/core": "^2.6.6",
"@tiptap/html": "^2.6.6",
"@tiptap/pm": "^2.6.6",
"@tiptap/starter-kit": "^2.6.6",
"@tiptap/extension-gapcursor": "^2.6.6",
"@tiptap/extension-heading": "^2.6.6",
"@tiptap/extension-highlight": "^2.6.6",
"@tiptap/extension-history": "^2.6.6",
"@tiptap/extension-link": "^2.6.6",
"@tiptap/extension-mention": "^2.6.6",
"@tiptap/extension-table": "^2.6.6",
"@tiptap/extension-table-cell": "^2.6.6",
"@tiptap/extension-table-header": "^2.6.6",
"@tiptap/extension-table-row": "^2.6.6",
"@tiptap/extension-task-item": "^2.6.6",
"@tiptap/extension-task-list": "^2.6.6",
"@tiptap/extension-typography": "^2.6.6",
"@tiptap/extension-code-block": "^2.6.6",
"@tiptap/extension-code": "^2.6.6",
"@tiptap/extension-underline": "^2.6.6",
"@tiptap/suggestion": "^2.6.6",
"@tiptap/core": "^2.11.3",
"@tiptap/html": "^2.11.3",
"@tiptap/pm": "^2.11.3",
"@tiptap/starter-kit": "^2.11.3",
"@tiptap/extension-gapcursor": "^2.11.3",
"@tiptap/extension-heading": "^2.11.3",
"@tiptap/extension-highlight": "^2.11.3",
"@tiptap/extension-history": "^2.11.3",
"@tiptap/extension-link": "^2.11.3",
"@tiptap/extension-mention": "^2.11.3",
"@tiptap/extension-table": "^2.11.3",
"@tiptap/extension-table-cell": "^2.11.3",
"@tiptap/extension-table-header": "^2.11.3",
"@tiptap/extension-table-row": "^2.11.3",
"@tiptap/extension-task-item": "^2.11.3",
"@tiptap/extension-task-list": "^2.11.3",
"@tiptap/extension-typography": "^2.11.3",
"@tiptap/extension-code-block": "^2.11.3",
"@tiptap/extension-code": "^2.11.3",
"@tiptap/extension-underline": "^2.11.3",
"@tiptap/suggestion": "^2.11.3",
"prosemirror-codemark": "^0.4.2",
"markdown-it": "^14.0.0",
"fast-equals": "^5.0.1",
"fast-equals": "^5.2.2",
"@tiptap/extension-text-align": "~2.11.0",
"@tiptap/extension-text-style": "~2.11.0"
},
+1 -1
View File
@@ -43,7 +43,7 @@
"@hcengineering/theme": "^0.6.5",
"@hcengineering/core": "^0.6.32",
"svelte": "^4.2.19",
"fast-equals": "^5.0.1",
"fast-equals": "^5.2.2",
"autolinker": "4.0.0",
"emoji-regex": "^10.1.0",
"date-fns": "^2.30.0",
@@ -51,6 +51,11 @@
<span class="fs-title overflow-label" class:content-color={contentColor}>
{#if label}<Label {label} />{/if}<slot name="title" />
</span>
{#if $$slots['title-tools']}
<div class="buttons-group small-gap">
<slot name="title-tools" />
</div>
{/if}
</div>
{#if $$slots.tools}
<div class="buttons-group small-gap">
@@ -20,7 +20,7 @@
DisplayActivityMessage,
WithReferences
} from '@hcengineering/activity'
import { Doc, Ref, SortingOrder } from '@hcengineering/core'
import { Class, Doc, Ref, SortingOrder } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Grid, Label, Section, Spinner, location, Lazy } from '@hcengineering/ui'
import { onDestroy, onMount } from 'svelte'
@@ -39,6 +39,7 @@
export let boundary: HTMLElement | undefined = undefined
const client = getClient()
const hierarchy = client.getHierarchy()
const activityMessagesQuery = createQuery()
const refsQuery = createQuery()
@@ -170,9 +171,24 @@
let isNewestFirst = JSON.parse(localStorage.getItem('activity-newest-first') ?? 'false')
$: void client.findAll(activity.class.ActivityExtension, { ofClass: object._class }).then((res) => {
extensions = res
})
$: extensions = getExtensions(object._class)
function getExtensions (_class: Ref<Class<Doc>>): ActivityExtension[] {
try {
let clazz: Ref<Class<Doc>> | undefined = _class
while (clazz !== undefined) {
const res = client.getModel().findAllSync(activity.class.ActivityExtension, { ofClass: clazz })
if (res.length > 0) {
return res
}
clazz = hierarchy.getClass(_class).extends
}
} catch (e) {
console.error(e)
return []
}
return []
}
// Load references from other spaces separately because they can have any different spaces
$: if ((object.references ?? 0) > 0) {
@@ -13,7 +13,7 @@
// limitations under the License.
// -->
<script lang="ts">
import { getJsonOrEmpty, type LinkPreviewDetails, canDisplayLinkPreview } from '@hcengineering/presentation'
import { getJsonOrEmpty, type LinkPreviewDetails } from '@hcengineering/presentation'
import { type Attachment } from '@hcengineering/attachment'
import { type WithLookup } from '@hcengineering/core'
import { Spinner } from '@hcengineering/ui'
@@ -22,12 +22,27 @@
export let attachment: WithLookup<Attachment>
let useDefaultIcon = false
let retryCount = 0
let viewModel: LinkPreviewDetails
let previewImageSrc: string | undefined
function refreshPreviewImage (): void {
if (viewModel?.image === undefined) {
return
}
if (retryCount > 3) {
previewImageSrc = undefined
return
}
retryCount++
previewImageSrc = `${viewModel.image}#${Date.now()}`
}
onMount(() => {
void getJsonOrEmpty(attachment.file, attachment.name)
.then((res) => {
viewModel = res as LinkPreviewDetails
refreshPreviewImage()
})
.catch((err) => {
console.error(err)
@@ -63,9 +78,16 @@
{#if viewModel.description}
{viewModel.description}
{/if}
{#if viewModel.image}
{#if previewImageSrc}
<a target="_blank" href={viewModel.url}>
<img src={viewModel.image} class="round-image" alt="link-preview" />
<img
src={previewImageSrc}
class="round-image"
alt="link-preview"
on:error={() => {
refreshPreviewImage()
}}
/>
</a>
{/if}
</div>
+1 -1
View File
@@ -57,7 +57,7 @@
"qs": "~6.11.0",
"@hcengineering/tags": "^0.6.16",
"@hcengineering/tags-resources": "^0.6.0",
"fast-equals": "^5.0.1",
"fast-equals": "^5.2.2",
"@hcengineering/recruit": "^0.6.29",
"@hcengineering/task": "^0.6.20"
},
+1 -1
View File
@@ -48,7 +48,7 @@
"@hcengineering/recruit": "^0.6.29",
"@hcengineering/tags": "^0.6.16",
"@hcengineering/task": "^0.6.20",
"fast-equals": "^5.0.1",
"fast-equals": "^5.2.2",
"qs": "~6.11.0"
},
"repository": "https://github.com/hcengineering/platform",
+1 -1
View File
@@ -54,7 +54,7 @@
"@hcengineering/view-resources": "^0.6.0",
"@hcengineering/view": "^0.6.13",
"@hcengineering/workbench": "^0.6.16",
"fast-equals": "^5.0.1",
"fast-equals": "^5.2.2",
"date-fns": "^2.30.0",
"date-fns-tz": "^2.0.0"
}
+2 -2
View File
@@ -58,10 +58,10 @@
"@hcengineering/notification": "^0.6.23",
"@hcengineering/contact": "^0.6.24",
"@hcengineering/workbench": "^0.6.16",
"@tiptap/core": "^2.6.6",
"@tiptap/core": "^2.11.3",
"@hcengineering/platform": "^0.6.11",
"@hcengineering/card": "^0.6.0",
"fast-equals": "^5.0.1",
"fast-equals": "^5.2.2",
"svelte": "^4.2.19"
}
}
+1 -1
View File
@@ -63,7 +63,7 @@
"@hcengineering/workbench": "^0.6.16",
"@hcengineering/workbench-resources": "^0.6.1",
"@hcengineering/presence-resources": "^0.6.0",
"fast-equals": "^5.0.1",
"fast-equals": "^5.2.2",
"svelte": "^4.2.19",
"@hcengineering/text-editor-resources": "^0.6.0",
"@hcengineering/text-editor": "^0.6.0"
@@ -35,7 +35,24 @@
const hierarchy = client.getHierarchy()
let extensions: ActivityExtension[] = []
$: extensions = client.getModel().findAllSync(activity.class.ActivityExtension, { ofClass: object._class })
$: extensions = getExtensions(object._class)
function getExtensions (_class: Ref<Class<Doc>>): ActivityExtension[] {
try {
let clazz: Ref<Class<Doc>> | undefined = _class
while (clazz !== undefined) {
const res = client.getModel().findAllSync(activity.class.ActivityExtension, { ofClass: clazz })
if (res.length > 0) {
return res
}
clazz = hierarchy.getClass(_class).extends
}
} catch (e) {
console.error(e)
return []
}
return []
}
let icon: Asset | AnySvelteComponent | undefined = undefined
-18
View File
@@ -629,24 +629,6 @@ export async function createDirect (employeeIds: Array<Ref<Employee>>): Promise<
if (context.hidden) {
await client.updateDoc(context._class, context.space, context._id, { hidden: false })
}
return dmId
}
const space = await client.findOne(
contact.class.PersonSpace,
{ person: me.person as Ref<Person> },
{ projection: { _id: 1 } }
)
if (space == null) return dmId
await client.createDoc(notification.class.DocNotifyContext, space._id, {
user: me._id,
objectId: dmId,
objectClass: chunter.class.DirectMessage,
objectSpace: core.space.Space,
hidden: false,
isPinned: false
})
return dmId
}
+1 -1
View File
@@ -45,7 +45,7 @@
"@hcengineering/ui": "^0.6.15",
"@hcengineering/view": "^0.6.13",
"@hcengineering/workbench": "^0.6.16",
"fast-equals": "^5.0.1"
"fast-equals": "^5.2.2"
},
"repository": "https://github.com/hcengineering/platform",
"publishConfig": {
+8
View File
@@ -60,6 +60,14 @@ if (typeof localStorage !== 'undefined') {
resolve(db)
}
})
void dbPromise.then((res) => {
if (res !== undefined) {
res.onclose = () => {
dbRequest = undefined
dbPromise = Promise.resolve(undefined)
}
}
})
}
/**
@@ -295,7 +295,12 @@
"CreateFolder": "Vytvořit novou složku",
"RenameFolder": "Přejmenovat složku",
"CreateChildFolder": "Vytvořit podsložku"
"CreateChildFolder": "Vytvořit podsložku",
"Obsolete": "Zastaralé",
"MakeDocumentObsolete": "Označit jako zastaralé",
"MakeDocumentObsoleteDialog": "Označit {count, plural, one {dokument jako zastaralý} other {dokumenty jako zastaralé}}",
"MakeDocumentObsoleteConfirm": "Opravdu chcete označit následující dokumenty jako zastaralé: {titles}?"
},
"controlledDocStates": {
"Empty": "",
@@ -302,7 +302,12 @@
"CreateFolder": "Neuen Ordner erstellen",
"RenameFolder": "Ordner umbenennen",
"CreateChildFolder": "Unterordner erstellen"
"CreateChildFolder": "Unterordner erstellen",
"Obsolete": "Veraltet",
"MakeDocumentObsolete": "Als veraltet markieren",
"MakeDocumentObsoleteDialog": "{count, plural, one {Dokument als veraltet markieren} other {Dokumente als veraltet markieren}}",
"MakeDocumentObsoleteConfirm": "Möchten Sie die folgenden Dokumente wirklich als veraltet markieren: {titles}?"
},
"controlledDocStates": {
"Empty": "",
@@ -119,6 +119,11 @@
"Archived": "Archived",
"Deleted": "Deleted",
"MetaAbstract": "Abstract",
"Obsolete": "Obsolete",
"MakeDocumentObsolete": "Mark as obsolete",
"MakeDocumentObsoleteDialog": "Mark {count, plural, =0 {document} other {documents}} as obsolete",
"MakeDocumentObsoleteConfirm": "Do you really want to mark the following documents as obsolete: {titles}?",
"ContentTab": "Content",
"TeamTab": "Team",
@@ -262,7 +262,12 @@
"CreateFolder": "Créer un nouveau dossier",
"RenameFolder": "Renommer le dossier",
"CreateChildFolder": "Créer un sous-dossier"
"CreateChildFolder": "Créer un sous-dossier",
"Obsolete": "Obsolète",
"MakeDocumentObsolete": "Marquer comme obsolète",
"MakeDocumentObsoleteDialog": "Marquer {count, plural, one {le document comme obsolète} other {les documents comme obsolètes}}",
"MakeDocumentObsoleteConfirm": "Voulez-vous vraiment marquer les documents suivants comme obsolètes : {titles} ?"
},
"controlledDocStates": {
"Empty": "",
@@ -260,7 +260,12 @@
"CreateFolder": "Crea nuova cartella",
"RenameFolder": "Rinomina cartella",
"CreateChildFolder": "Crea sottocartella"
"CreateChildFolder": "Crea sottocartella",
"Obsolete": "Obsoleto",
"MakeDocumentObsolete": "Segna come obsoleto",
"MakeDocumentObsoleteDialog": "Segna {count, plural, one {il documento come obsoleto} other {i documenti come obsoleti}}",
"MakeDocumentObsoleteConfirm": "Vuoi davvero segnare i seguenti documenti come obsoleti: {titles}?"
},
"controlledDocStates": {
"Empty": "",
@@ -304,7 +304,12 @@
"CreateFolder": "Создать новую папку",
"RenameFolder": "Переименовать папку",
"CreateChildFolder": "Создать подпапку"
"CreateChildFolder": "Создать подпапку",
"Obsolete": "Устаревший",
"MakeDocumentObsolete": "Пометить как устаревшее",
"MakeDocumentObsoleteDialog": "Пометить {count, plural, one {документ как устаревший} other {документы как устаревшие}}",
"MakeDocumentObsoleteConfirm": "Вы действительно хотите пометить следующие документы как устаревшие: {titles}?"
},
"controlledDocStates": {
"Empty": "",
@@ -301,7 +301,12 @@
"CreateFolder": "创建新文件夹",
"RenameFolder": "重命名文件夹",
"CreateChildFolder": "创建子文件夹"
"CreateChildFolder": "创建子文件夹",
"Obsolete": "已过时",
"MakeDocumentObsolete": "标记为过时",
"MakeDocumentObsoleteDialog": "标记 {count, plural, one {文档为过时} other {文档为过时}}",
"MakeDocumentObsoleteConfirm": "您确定要将以下文档标记为过时吗:{titles}?"
},
"controlledDocStates": {
"Empty": "",
@@ -66,11 +66,11 @@
"@hcengineering/controlled-documents": "^0.1.0",
"@hcengineering/training": "^0.1.0",
"@hcengineering/training-resources": "^0.1.0",
"@tiptap/core": "^2.6.6",
"@tiptap/core": "^2.11.3",
"effector": "~22.8.7",
"svelte": "^4.2.19",
"slugify": "^1.6.6",
"fast-equals": "^5.0.1",
"fast-equals": "^5.2.2",
"@hcengineering/rank": "^0.6.4",
"@hcengineering/print": "^0.6.0"
}
@@ -57,9 +57,10 @@
$: inProgress = { state: { $in: [DocumentState.Draft] }, space: { $in: spaces } }
$: effective = { state: { $in: [DocumentState.Effective] }, space: { $in: spaces } }
$: archived = { state: { $in: [DocumentState.Archived, DocumentState.Deleted] }, space: { $in: spaces } }
$: obsolete = { state: { $in: [DocumentState.Obsolete] }, space: { $in: spaces } }
$: all = { space: { $in: spaces } }
$: queries = { inProgress, effective, archived, all }
$: queries = { inProgress, effective, archived, obsolete, all }
$: mode = $resolvedLocationStore.query?.mode ?? undefined
$: if (mode === undefined || (queries as any)[mode] === undefined) {
@@ -118,6 +118,7 @@
DocumentState.Archived,
DocumentState.Deleted,
DocumentState.Effective,
DocumentState.Obsolete,
ControlledDocumentState.InApproval,
ControlledDocumentState.Approved,
ControlledDocumentState.ToReview
@@ -37,11 +37,11 @@
}
&.obsolete {
background: var(--theme-state-ghost-background-color);
border-color: var(--theme-state-ghost-border-color);
background: var(--theme-state-negative-background-color);
border-color: var(--theme-state-negative-border-color);
.label {
color: var(--theme-state-ghost-color);
color: var(--theme-state-negative-color);
}
}
@@ -0,0 +1,48 @@
<!--
// Copyright © 2025 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import documents, { DocumentCategory, DocumentTemplate } from '@hcengineering/controlled-documents'
import { createQuery, getClient } from '@hcengineering/presentation'
import { createEventDispatcher } from 'svelte'
import { Ref } from '@hcengineering/core'
import { DropdownLabelsPopup } from '@hcengineering/ui'
export let object: DocumentTemplate
const client = getClient()
const dispatch = createEventDispatcher()
let categories: DocumentCategory[] = []
const query = createQuery()
query.query(documents.class.DocumentCategory, {}, (res) => {
categories = res
})
async function handleSubmit (category: Ref<DocumentCategory>): Promise<void> {
await client.update(object, { category })
dispatch('close')
}
</script>
{#if object}
<DropdownLabelsPopup
items={categories.map((cat) => ({ id: cat._id, label: cat.title }))}
selected={object.category}
on:close={(e) => handleSubmit(e.detail)}
/>
{/if}
@@ -5,21 +5,41 @@
import { getClient } from '@hcengineering/presentation'
import { Label } from '@hcengineering/ui'
import view from '@hcengineering/view'
import { createEventDispatcher } from 'svelte'
export let value: Ref<DocumentCategory> | undefined
export let editable: boolean = false
let category: DocumentCategory | undefined = undefined
const client = getClient()
const dispatch = createEventDispatcher()
$: if (value) {
client.findOne(documents.class.DocumentCategory, { _id: value }).then((result) => {
category = result
})
}
function handleClick (event: MouseEvent): void {
if (!editable) {
return
}
dispatch('edit', event)
}
</script>
{#if category}
{category.title}
<a
class="flex-presenter inline-presenter noBold"
class:no-underline={!editable}
class:cursor-inherit={!editable}
href={undefined}
on:click={handleClick}
>
{category.title}
</a>
{:else}
<Label label={view.string.LabelNA} />
{/if}
@@ -22,6 +22,7 @@
import documentsRes from '../../../plugin'
import {
$documentAllVersionsDescSorted as documentAllVersions,
$controlledDocument as controlledDocument,
$isEditable as isEditable,
$projectRef as projectRef
@@ -41,6 +42,7 @@
import AbstractEditor from '../editors/AbstractEditor.svelte'
import DocumentFlatHierarchy from './info/DocumentFlatHierarchy.svelte'
import DocumentPrefixPresenter from '../presenters/DocumentPrefixPresenter.svelte'
import ChangeCategoryPopup from '../popups/ChangeCategoryPopup.svelte'
const client = getClient()
const hierarchy = client.getHierarchy()
@@ -58,9 +60,20 @@
)
}
$: isDocCodeEditable =
$isEditable && $controlledDocument != null && $controlledDocument.major === 0 && $controlledDocument.minor === 0
$: isDocPrefixEditable = isDocCodeEditable
function handleCategoryEdit (event: MouseEvent): void {
event?.preventDefault()
event?.stopPropagation()
showPopup(
ChangeCategoryPopup,
{
object: $controlledDocument
},
eventToHTMLElement(event)
)
}
$: isEditableDraft = $isEditable && $controlledDocument != null && $documentAllVersions.length === 1
$: isTemplate =
$controlledDocument != null && hierarchy.hasMixin($controlledDocument, documents.mixin.DocumentTemplate)
@@ -81,7 +94,7 @@
value={$controlledDocument}
isRegular
disableLink
editable={isDocCodeEditable}
editable={isEditableDraft}
on:edit={(e) => {
handleCodeEdit(e.detail)
}}
@@ -89,7 +102,13 @@
</DocumentInfo>
<DocumentInfo label={documentsRes.string.Category}>
<CategoryPresenter value={$controlledDocument.category} />
<CategoryPresenter
value={$controlledDocument.category}
editable={isEditableDraft}
on:edit={(e) => {
handleCategoryEdit(e.detail)
}}
/>
</DocumentInfo>
{#if !isTemplate}
@@ -100,7 +119,7 @@
{#if isTemplate}
<DocumentInfo label={documentsRes.string.DocumentPrefix}>
<DocumentPrefixPresenter value={asTemplate} editable={isDocPrefixEditable} />
<DocumentPrefixPresenter value={asTemplate} editable={isEditableDraft} />
</DocumentInfo>
{/if}
@@ -148,9 +148,10 @@
{@const title = doc ? getDocumentName(doc) : meta?.title ?? ''}
{@const docid = doc?._id ?? prjdoc._id}
{@const isFolder = prjdoc.document === documents.ids.Folder}
{@const isObsolete = doc ? doc.state === DocumentState.Obsolete : false}
{@const children = metaid ? childrenByParent[metaid] ?? [] : []}
{#if metaid}
{@const children = childrenByParent[metaid] ?? []}
{#if metaid && (!isObsolete || children.length > 0)}
{@const isDraggedOver = draggedOver === metaid}
<div class="flex-col relative">
{#if isDraggedOver}
@@ -160,12 +161,12 @@
_id={docid}
icon={isFolder ? documents.icon.Folder : documents.icon.Document}
iconProps={{
fill: 'currentColor'
fill: isObsolete ? 'var(--dangerous-bg-color)' : 'currentColor'
}}
{title}
selected={selected === docid || selected === prjdoc._id}
isFold
empty={children.length === 0 || children === undefined}
empty={children.length === 0}
actions={getMoreActions !== undefined ? () => getDocMoreActions(prjdoc) : undefined}
{level}
{collapsedPrefix}
@@ -38,6 +38,9 @@
case DocumentState.Archived:
statusWMLabel = plugin.string.Archived
break
case DocumentState.Obsolete:
statusWMLabel = plugin.string.Obsolete
break
}
}
@@ -51,7 +54,7 @@
$controlledDocument != null &&
(isOrgSpace
? $controlledDocument.state !== DocumentState.Effective
: ![DocumentState.Effective, DocumentState.Archived].includes($controlledDocument.state))
: ![DocumentState.Effective, DocumentState.Obsolete, DocumentState.Archived].includes($controlledDocument.state))
</script>
{#if $controlledDocument !== null}
@@ -182,6 +182,24 @@ async function archiveDocuments (obj: Document | Document[]): Promise<void> {
})
}
async function makeDocumentObsolete (obj: Document | Document[]): Promise<void> {
const docs = Array.isArray(obj) ? obj : [obj]
const docNames = docs.map((d) => `${d.title} (${d.prefix}-${d.seqNumber})`).join(', ')
showPopup(MessageBox, {
label: documents.string.MakeDocumentObsoleteDialog,
labelProps: { count: docs.length },
message: documents.string.MakeDocumentObsoleteConfirm,
params: { titles: docNames },
action: async () => {
const client = getClient()
for (const doc of docs) {
await client.update(doc, { state: DocumentState.Obsolete })
}
}
})
}
async function canDeleteDocument (obj?: Doc | Doc[]): Promise<boolean> {
if (obj == null) {
return false
@@ -220,6 +238,28 @@ async function canArchiveDocument (obj?: Doc | Doc[]): Promise<boolean> {
).then((res) => res.every((r) => r))
}
async function canMakeDocumentObsolete (obj?: Doc | Doc[]): Promise<boolean> {
if (obj == null) {
return false
}
const objs = (Array.isArray(obj) ? obj : [obj]) as Document[]
const currentUser = getCurrentAccount() as PersonAccount
const isOwner = objs.every((doc) => doc.owner === currentUser.person)
if (isOwner) {
return true
}
const spaces = new Set(objs.map((doc) => doc.space))
return await Promise.all(
Array.from(spaces).map(
async (space) => await checkPermission(getClient(), documents.permission.ArchiveDocument, space)
)
).then((res) => res.every((r) => r))
}
async function canOpenDocument (obj?: ProjectDocument | ProjectDocument[]): Promise<boolean> {
if (obj == null) {
return false
@@ -411,6 +451,7 @@ export default async (): Promise<Resources> => ({
GetDocumentMetaLinkFragment: getDocumentMetaLinkFragment,
CanDeleteDocument: canDeleteDocument,
CanArchiveDocument: canArchiveDocument,
CanMakeDocumentObsolete: canMakeDocumentObsolete,
CanTransferDocument: canTransferDocument,
CanOpenDocument: canOpenDocument,
CanPrintDocument: canPrintDocument,
@@ -430,6 +471,7 @@ export default async (): Promise<Resources> => ({
CreateFolder: createFolder,
DeleteDocument: deleteDocuments,
ArchiveDocument: archiveDocuments,
MakeDocumentObsolete: makeDocumentObsolete,
TransferDocument: transferDocuments,
EditDocSpace: editDocSpace
},
@@ -242,6 +242,7 @@ export default mergeIds(documentsId, documents, {
GetDocumentMetaLinkFragment: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
CanDeleteDocument: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
CanArchiveDocument: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
CanMakeDocumentObsolete: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
CanOpenDocument: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
CanPrintDocument: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
CanTransferDocument: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
@@ -21,9 +21,10 @@ export const $canCreateNewDraft = combine($controlledDocument, $documentAllVersi
if (document == null) return false
const currentIndex = versions.findIndex((p) => p._id === document._id)
const forbiddenStates = [DocumentState.Draft, DocumentState.Obsolete]
return (
versions.slice(0, currentIndex).every((p) => p.state === DocumentState.Deleted) &&
document.state !== DocumentState.Draft
!forbiddenStates.includes(document.state)
)
})
@@ -82,7 +82,8 @@ export async function getTranslatedDocumentStates (lang: string): Promise<Transl
[DocumentState.Draft]: await translate(documents.string.Draft, {}, lang),
[DocumentState.Deleted]: await translate(documents.string.Deleted, {}, lang),
[DocumentState.Effective]: await translate(documents.string.Effective, {}, lang),
[DocumentState.Archived]: await translate(documents.string.Archived, {}, lang)
[DocumentState.Archived]: await translate(documents.string.Archived, {}, lang),
[DocumentState.Obsolete]: await translate(documents.string.Obsolete, {}, lang)
}
}
@@ -159,6 +160,8 @@ export async function getDocumentMetaLinkFragment (document: Doc): Promise<Locat
break
} else if (doc.state === DocumentState.Deleted && targetDocument === undefined) {
targetDocument = doc
} else if (doc.state === DocumentState.Obsolete && targetDocument === undefined) {
targetDocument = doc
} else if (doc.state === DocumentState.Draft) {
targetDocument = doc
} else if (doc.state === DocumentState.Archived) {
@@ -363,13 +366,15 @@ export const statesTags: StatesTags = {
[DocumentState.Draft]: 'draft',
[DocumentState.Effective]: 'effective',
[DocumentState.Archived]: 'obsolete',
[DocumentState.Deleted]: 'obsolete'
[DocumentState.Deleted]: 'obsolete',
[DocumentState.Obsolete]: 'obsolete'
}
export const documentStatesOrder = [
DocumentState.Draft,
DocumentState.Effective,
DocumentState.Archived,
DocumentState.Obsolete,
DocumentState.Deleted
]
@@ -121,6 +121,7 @@ export const documentsPlugin = plugin(documentsId, {
DeleteDocumentCategory: '' as Ref<Action<Doc, any>>,
DeleteDocument: '' as Ref<Action>,
ArchiveDocument: '' as Ref<Action>,
MakeDocumentObsolete: '' as Ref<Action>,
EditDocSpace: '' as Ref<Action>,
TransferDocument: '' as Ref<Action>,
Print: '' as Ref<Action<Doc, { signed: boolean }>>,
@@ -202,6 +203,10 @@ export const documentsPlugin = plugin(documentsId, {
Deleted: '' as IntlString,
Effective: '' as IntlString,
Archived: '' as IntlString,
Obsolete: '' as IntlString,
MakeDocumentObsolete: '' as IntlString,
MakeDocumentObsoleteDialog: '' as IntlString,
MakeDocumentObsoleteConfirm: '' as IntlString,
Parent: '' as IntlString,
Template: '' as IntlString,
GeneralInfo: '' as IntlString,
+2 -1
View File
@@ -194,7 +194,8 @@ export enum DocumentState {
Draft = 'draft',
Effective = 'effective',
Archived = 'archived',
Deleted = 'deleted'
Deleted = 'deleted',
Obsolete = 'obsolete'
}
/**
+1 -1
View File
@@ -47,7 +47,7 @@
"@hcengineering/view-resources": "^0.6.0",
"@hcengineering/highlight": "^0.6.0",
"@hcengineering/diffview": "^0.6.0",
"fast-equals": "^5.0.1",
"fast-equals": "^5.2.2",
"diff2html": "~3.4.35"
}
}
+2 -2
View File
@@ -63,8 +63,8 @@
"@hcengineering/document": "^0.6.0",
"@hcengineering/time": "^0.6.0",
"@hcengineering/rank": "^0.6.4",
"@tiptap/core": "^2.6.6",
"@tiptap/core": "^2.11.3",
"slugify": "^1.6.6",
"fast-equals": "^5.0.1"
"fast-equals": "^5.2.2"
}
}
+1 -1
View File
@@ -50,6 +50,6 @@
"@hcengineering/view": "^0.6.13",
"@hcengineering/view-resources": "^0.6.0",
"svelte": "^4.2.19",
"fast-equals": "^5.0.1"
"fast-equals": "^5.2.2"
}
}
+1 -1
View File
@@ -50,6 +50,6 @@
"@hcengineering/login": "^0.6.12",
"@hcengineering/view-resources": "^0.6.0",
"@hcengineering/analytics": "^0.6.0",
"fast-copy": "~3.0.1"
"fast-copy": "^3.0.2"
}
}
+1 -1
View File
@@ -61,6 +61,6 @@
"@hcengineering/view-resources": "^0.6.0",
"@hcengineering/workbench": "^0.6.16",
"svelte": "^4.2.19",
"fast-equals": "^5.0.1"
"fast-equals": "^5.2.2"
}
}
@@ -1,5 +1,14 @@
<script lang="ts">
import { groupByArray, isActiveMode, type BaseWorkspaceInfo } from '@hcengineering/core'
import {
groupByArray,
isActiveMode,
isArchivingMode,
isDeletingMode,
isMigrationMode,
isRestoringMode,
reduceCalls,
type BaseWorkspaceInfo
} from '@hcengineering/core'
import { getEmbeddedLabel } from '@hcengineering/platform'
import { isAdminUser } from '@hcengineering/presentation'
import {
@@ -14,7 +23,8 @@
Popup,
Scroller,
SearchEdit,
ticker
ticker,
CheckBox
} from '@hcengineering/ui'
import { workbenchId } from '@hcengineering/workbench'
import { getAllWorkspaces, getRegionInfo, performWorkspaceOperation, type RegionInfo } from '../utils'
@@ -32,13 +42,14 @@
let workspaces: WorkspaceInfo[] = []
$: if ($ticker > 0) {
void getAllWorkspaces().then((res) => {
workspaces = res.sort((a, b) =>
(b.workspaceUrl ?? b.workspace).localeCompare(a.workspaceUrl ?? a.workspace)
) as WorkspaceInfo[]
})
}
const updateWorkspaces = reduceCalls(async (_: number) => {
const res = await getAllWorkspaces()
workspaces = res.sort((a, b) =>
(b.workspaceUrl ?? b.workspace).localeCompare(a.workspaceUrl ?? a.workspace)
) as WorkspaceInfo[]
})
$: void updateWorkspaces($ticker)
const now = Date.now()
@@ -55,13 +66,26 @@
FewOrMoreYears: 10000000
}
let limit = 50
// Individual filters
let showActive: boolean = true
let showArchived: boolean = false
let showDeleted: boolean = true
let showOther: boolean = true
$: groupped = groupByArray(
workspaces.filter(
(it) =>
(it.workspaceName?.includes(search) ?? false) ||
(it.workspaceUrl?.includes(search) ?? false) ||
it.workspace?.includes(search) ||
it.createdBy?.includes(search)
((it.workspaceName?.includes(search) ?? false) ||
(it.workspaceUrl?.includes(search) ?? false) ||
it.workspace?.includes(search) ||
it.createdBy?.includes(search)) &&
((showActive && isActiveMode(it.mode)) ||
(showArchived && isArchivingMode(it.mode)) ||
(showDeleted && isDeletingMode(it.mode)) ||
(showOther && (isMigrationMode(it.mode) || isRestoringMode(it.mode))))
),
(it) => {
const lastUsageDays = Math.round((now - it.lastVisit) / (1000 * 3600 * 24))
@@ -92,6 +116,26 @@
<SearchEdit bind:value={search} width={'100%'} />
</div>
<div class="p-3 flex-col">
<span class="fs-title mr-2">Filters: </span>
<div class="flex-row-center">
Show active workspaces:
<CheckBox bind:checked={showActive} />
</div>
<div class="flex-row-center">
<span class="mr-2">Show archived workspaces:</span>
<CheckBox bind:checked={showArchived} />
</div>
<div class="flex-row-center">
<span class="mr-2">Show deleted workspaces:</span>
<CheckBox bind:checked={showDeleted} />
</div>
<div class="flex-row-center">
<span class="mr-2">Show other workspaces:</span>
<CheckBox bind:checked={showOther} />
</div>
</div>
<div class="fs-title p-3 flex-row-center">
<span class="mr-2"> Migration region selector: </span>
<ButtonMenu
@@ -109,6 +153,7 @@
<div class="mr-4">
{#each Object.keys(dayRanges) as k}
{@const v = groupped.get(k) ?? []}
{@const hasMore = (groupped.get(k) ?? []).length > limit}
{@const activeV = v.filter((it) => it.mode === 'active' && (it.region ?? '') !== selectedRegionId)}
{@const archiveV = v.filter((it) => it.mode === 'active')}
{@const archivedD = v.filter((it) => it.mode === 'archived')}
@@ -116,13 +161,31 @@
{#if v.length > 0}
<Expandable expandable={true} bordered={true}>
<svelte:fragment slot="title">
<span class="fs-title focused-button">
{k} - {v.length}
<span class="fs-title focused-button flex-row-center">
{k} -
{#if hasMore}
{limit} of {v.length}
{:else}
{v.length}
{/if}
{#if av > 0}
- maitenance: {av}
{/if}
</span>
</svelte:fragment>
<svelte:fragment slot="title-tools">
{#if hasMore}
<div class="ml-4">
<Button
label={getEmbeddedLabel(`More ${k}`)}
kind={'link'}
on:click={() => {
limit += 50
}}
/>
</div>
{/if}
</svelte:fragment>
<svelte:fragment slot="tools">
{#if archiveV.length > 0}
<Button
@@ -153,7 +216,7 @@
/>
{/if}
</svelte:fragment>
{#each v as workspace}
{#each v.slice(0, limit) as workspace}
{@const wsName = workspace.workspaceName ?? workspace.workspace}
{@const lastUsageDays = Math.round((Date.now() - workspace.lastVisit) / (1000 * 3600 * 24))}
<!-- svelte-ignore a11y-click-events-have-key-events -->
+3 -3
View File
@@ -59,9 +59,9 @@
"@hcengineering/view-resources": "^0.6.0",
"@hcengineering/workbench": "^0.6.16",
"@hcengineering/workbench-resources": "^0.6.1",
"@livekit/krisp-noise-filter": "~0.2.13",
"@livekit/track-processors": "~0.3.3",
"livekit-client": "^2.7.5",
"@livekit/krisp-noise-filter": "^0.2.16",
"@livekit/track-processors": "^0.3.3",
"livekit-client": "^2.8.1",
"svelte": "^4.2.19"
}
}
+1 -1
View File
@@ -49,6 +49,6 @@
"@hcengineering/contact-resources": "^0.6.0",
"@hcengineering/presence": "^0.6.0",
"svelte": "^4.2.19",
"fast-equals": "^5.0.1"
"fast-equals": "^5.2.2"
}
}
+1 -1
View File
@@ -55,6 +55,6 @@
"@hcengineering/tags": "^0.6.16",
"@hcengineering/contact-resources": "^0.6.0",
"@hcengineering/notification": "^0.6.23",
"fast-equals": "^5.0.1"
"fast-equals": "^5.2.2"
}
}
+1 -1
View File
@@ -45,7 +45,7 @@
"@hcengineering/ui": "^0.6.15",
"@hcengineering/view": "^0.6.13",
"@hcengineering/view-resources": "^0.6.0",
"fast-equals": "^5.0.1",
"fast-equals": "^5.2.2",
"lexorank": "~1.0.4",
"svelte": "^4.2.19"
}
+2 -1
View File
@@ -121,7 +121,8 @@
"HideDoneState": "Skrýt dokončené přihlášky",
"HideArchivedVacancies": "Skrýt archivované pozice",
"HideApplicantsFromArchivedVacancies": "Skrýt z archivovaných pozic",
"CreateNewSkills": "Vytvořit nové dovednosti, pokud neexistují"
"CreateNewSkills": "Vytvořit nové dovednosti, pokud neexistují",
"SwapFirstAndLastNames": "Vyměňte jméno a příjmení"
},
"status": {
"ApplicationExists": "Přihláška již existuje",
+2 -1
View File
@@ -121,7 +121,8 @@
"HideDoneState": "Abgeschlossene Bewerbungen ausblenden",
"HideArchivedVacancies": "Archivierte Stellen ausblenden",
"HideApplicantsFromArchivedVacancies": "Aus archivierten Stellen ausblenden",
"CreateNewSkills": "Neue Fähigkeiten erstellen, wenn keine bestehenden gefunden werden"
"CreateNewSkills": "Neue Fähigkeiten erstellen, wenn keine bestehenden gefunden werden",
"SwapFirstAndLastNames": "Vor- und Nachnamen tauschen"
},
"status": {
"ApplicationExists": "Bewerbung existiert bereits",
+2 -1
View File
@@ -121,7 +121,8 @@
"HideDoneState": "Hide complete applications",
"HideArchivedVacancies": "Hide archived Vacancies",
"HideApplicantsFromArchivedVacancies": "Hide from archived Vacancies",
"CreateNewSkills": "Create new skills if existing not found"
"CreateNewSkills": "Create new skills if existing not found",
"SwapFirstAndLastNames": "Swap first and last names"
},
"status": {
"ApplicationExists": "Application already exists",
+2 -1
View File
@@ -118,7 +118,8 @@
"HideDoneState": "Ocultar solicitudes completadas",
"HideArchivedVacancies": "Ocultar vacantes archivadas",
"HideApplicantsFromArchivedVacancies": "Ocultar de vacantes archivadas",
"CreateNewSkills": "Crear nuevas habilidades si no se encuentran las existentes"
"CreateNewSkills": "Crear nuevas habilidades si no se encuentran las existentes",
"SwapFirstAndLastNames": "Intercambie nombres y apellidos"
},
"status": {
"ApplicationExists": "La solicitud ya existe",
+2 -1
View File
@@ -118,7 +118,8 @@
"HideDoneState": "Masquer les candidatures terminées",
"HideArchivedVacancies": "Masquer les postes vacants archivés",
"HideApplicantsFromArchivedVacancies": "Masquer les candidats des postes vacants archivés",
"CreateNewSkills": "Créer de nouvelles compétences si les existantes ne sont pas trouvées"
"CreateNewSkills": "Créer de nouvelles compétences si les existantes ne sont pas trouvées",
"SwapFirstAndLastNames": "Permuter le prénom et le nom"
},
"status": {
"ApplicationExists": "La candidature existe déjà",
+2 -1
View File
@@ -119,7 +119,8 @@
"HideDoneState": "Nascondi candidature completate",
"HideArchivedVacancies": "Nascondi posizioni archiviate",
"HideApplicantsFromArchivedVacancies": "Nascondi da posizioni archiviate",
"CreateNewSkills": "Crea nuove competenze se quelle esistenti non vengono trovate"
"CreateNewSkills": "Crea nuove competenze se quelle esistenti non vengono trovate",
"SwapFirstAndLastNames": "Scambia nome e cognome"
},
"status": {
"ApplicationExists": "La candidatura esiste già",
+2 -1
View File
@@ -118,7 +118,8 @@
"HideDoneState": "Ocultar candidaturas concluídas",
"HideArchivedVacancies": "Ocultar vagas arquivadas",
"HideApplicantsFromArchivedVacancies": "Ocultar de vagas arquivadas",
"CreateNewSkills": "Criar novas competências se as existentes não forem encontradas"
"CreateNewSkills": "Criar novas competências se as existentes não forem encontradas",
"SwapFirstAndLastNames": "Trocar primeiro e último nome"
},
"status": {
"ApplicationExists": "A candidatura já existe",
+2 -1
View File
@@ -121,7 +121,8 @@
"HideDoneState": "Скрыть завершенных кандидатов",
"HideArchivedVacancies": "Скрыть архивные вакансии",
"HideApplicantsFromArchivedVacancies": "Скрыть из архивных вакансии",
"CreateNewSkills": "Создать навыки, если не найдены существующие"
"CreateNewSkills": "Создать навыки, если не найдены существующие",
"SwapFirstAndLastNames": "Поменять местами имя и фамилию"
},
"status": {
"ApplicationExists": "Кандидат уже существует",
+2 -1
View File
@@ -121,7 +121,8 @@
"HideDoneState": "隐藏已完成的申请",
"HideArchivedVacancies": "隐藏已归档的职位",
"HideApplicantsFromArchivedVacancies": "从已归档职位中隐藏",
"CreateNewSkills": "如果未找到现有技能,则创建新技能"
"CreateNewSkills": "如果未找到现有技能,则创建新技能",
"SwapFirstAndLastNames": "交换名字和姓氏"
},
"status": {
"ApplicationExists": "申请已存在",
@@ -67,12 +67,15 @@
Label,
MiniToggle,
showPopup,
Spinner
Spinner,
ActionIcon
} from '@hcengineering/ui'
import { createEventDispatcher, onDestroy } from 'svelte'
import recruit from '../plugin'
import { getCandidateIdentifier } from '../utils'
import YesNo from './YesNo.svelte'
import IconSwitch from './icons/Switch.svelte'
import IconShuffle from './icons/Shuffle.svelte'
export let shouldSaveDraft: boolean = true
@@ -631,7 +634,7 @@
maxWidth={'30rem'}
/>
</div>
<div class="ml-4">
<div class="flex-col items-center flex-gap-2 ml-4">
<EditableAvatar
disabled={loading}
bind:this={avatarEditor}
@@ -642,6 +645,16 @@
size={'large'}
name={combineName(object?.firstName?.trim() ?? '', object?.lastName?.trim() ?? '')}
/>
<ActionIcon
icon={IconShuffle}
label={recruit.string.SwapFirstAndLastNames}
size={'medium'}
action={() => {
const first = object.firstName
object.firstName = object.lastName
object.lastName = first
}}
/>
</div>
</div>
<svelte:fragment slot="pool">
@@ -0,0 +1,26 @@
<!--
// Copyright © 2025 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 { IconSize } from '@hcengineering/ui'
export let size: IconSize
export let fill: string = 'currentColor'
</script>
<svg class="svg-{size}" {fill} viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path
d="M21.9,16.6c-0.1-0.1-0.1-0.2-0.2-0.3l-3-3c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4l1.3,1.3H16c-1.1,0-2.1-0.4-2.8-1.2C12.4,14.1,12,13.1,12,12s0.4-2.1,1.2-2.8C13.9,8.4,14.9,8,16,8h2.6l-1.3,1.3c-0.4,0.4-0.4,1,0,1.4c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3l3-3c0.1-0.1,0.2-0.2,0.2-0.3c0.1-0.2,0.1-0.5,0-0.8c-0.1-0.1-0.1-0.2-0.2-0.3l-3-3c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4L18.6,6H16c-1.6,0-3.1,0.6-4.2,1.8C11.5,8,11.2,8.4,11,8.7c-0.2-0.3-0.5-0.6-0.8-0.9C9.1,6.6,7.6,6,6,6H3C2.4,6,2,6.4,2,7s0.4,1,1,1h3c1.1,0,2.1,0.4,2.8,1.2C9.6,9.9,10,10.9,10,12s-0.4,2.1-1.2,2.8C8.1,15.6,7.1,16,6,16H3c-0.6,0-1,0.4-1,1s0.4,1,1,1h3c1.6,0,3.1-0.6,4.2-1.8c0.3-0.3,0.5-0.6,0.8-0.9c0.2,0.3,0.5,0.6,0.8,0.9c1.1,1.1,2.6,1.8,4.2,1.8h2.6l-1.3,1.3c-0.4,0.4-0.4,1,0,1.4c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3l3-3c0.1-0.1,0.2-0.2,0.2-0.3C22,17.1,22,16.9,21.9,16.6z"
/>
</svg>
+2 -1
View File
@@ -127,7 +127,8 @@ export default mergeIds(recruitId, recruit, {
OpenVacancyList: '' as IntlString,
Export: '' as IntlString,
GetTalentIds: '' as IntlString,
CreateNewSkills: '' as IntlString
CreateNewSkills: '' as IntlString,
SwapFirstAndLastNames: '' as IntlString
},
category: {
Other: '' as Ref<TagCategory>,
+1
View File
@@ -70,6 +70,7 @@ export default mergeIds(settingId, setting, {
AddOwner: '' as IntlString,
User: '' as IntlString,
Maintainer: '' as IntlString,
Guest: '' as IntlString,
Owner: '' as IntlString,
OwnerFirstName: '' as IntlString,
OwnerLastName: '' as IntlString,
@@ -71,7 +71,7 @@
"@hcengineering/view-resources": "^0.6.0",
"@hcengineering/workbench": "^0.6.16",
"@hcengineering/workbench-resources": "^0.6.1",
"fast-equals": "^5.0.1",
"fast-equals": "^5.2.2",
"svelte": "^4.2.19"
}
}
+26 -26
View File
@@ -50,40 +50,40 @@
"@hcengineering/text": "^0.6.5",
"@hcengineering/text-editor": "^0.6.0",
"@hcengineering/collaborator-client": "^0.6.4",
"@tiptap/core": "^2.6.6",
"@tiptap/pm": "^2.6.6",
"@tiptap/extension-code-block-lowlight": "^2.6.6",
"@tiptap/extension-collaboration": "^2.6.6",
"@tiptap/extension-collaboration-cursor": "^2.6.6",
"@tiptap/extension-placeholder": "^2.6.6",
"@tiptap/extension-hard-break": "^2.6.6",
"@tiptap/extension-bubble-menu": "^2.6.6",
"@tiptap/extension-table": "^2.6.6",
"@tiptap/extension-table-cell": "^2.6.6",
"@tiptap/extension-table-header": "^2.6.6",
"@tiptap/extension-table-row": "^2.6.6",
"@tiptap/extension-heading": "^2.6.6",
"@tiptap/extension-list-keymap": "^2.6.6",
"@tiptap/extension-code": "^2.6.6",
"@tiptap/extension-code-block": "^2.6.6",
"@tiptap/extension-highlight": "^2.6.6",
"@tiptap/extension-typography": "^2.6.6",
"@tiptap/extension-link": "^2.6.6",
"@tiptap/starter-kit": "^2.6.6",
"@tiptap/extension-underline": "^2.6.6",
"@tiptap/core": "^2.11.3",
"@tiptap/pm": "^2.11.3",
"@tiptap/extension-code-block-lowlight": "^2.11.3",
"@tiptap/extension-collaboration": "^2.11.3",
"@tiptap/extension-collaboration-cursor": "^2.11.3",
"@tiptap/extension-placeholder": "^2.11.3",
"@tiptap/extension-hard-break": "^2.11.3",
"@tiptap/extension-bubble-menu": "^2.11.3",
"@tiptap/extension-table": "^2.11.3",
"@tiptap/extension-table-cell": "^2.11.3",
"@tiptap/extension-table-header": "^2.11.3",
"@tiptap/extension-table-row": "^2.11.3",
"@tiptap/extension-heading": "^2.11.3",
"@tiptap/extension-list-keymap": "^2.11.3",
"@tiptap/extension-code": "^2.11.3",
"@tiptap/extension-code-block": "^2.11.3",
"@tiptap/extension-highlight": "^2.11.3",
"@tiptap/extension-typography": "^2.11.3",
"@tiptap/extension-link": "^2.11.3",
"@tiptap/starter-kit": "^2.11.3",
"@tiptap/extension-underline": "^2.11.3",
"@hocuspocus/provider": "^2.11.0",
"prosemirror-codemark": "^0.4.2",
"y-protocols": "^1.0.6",
"y-prosemirror": "^1.2.12",
"y-websocket": "^2.0.4",
"yjs": "^13.6.19",
"fast-equals": "^5.0.1",
"y-prosemirror": "^1.2.15",
"y-websocket": "^2.1.0",
"yjs": "^13.6.23",
"fast-equals": "^5.2.2",
"rfc6902": "^5.0.1",
"diff": "^5.1.0",
"slugify": "^1.6.6",
"lib0": "^0.2.88",
"y-indexeddb": "^9.0.12",
"lowlight": "^3.1.0",
"lowlight": "^3.3.0",
"mermaid": "~11.4.1",
"@hcengineering/theme": "^0.6.5",
"tippy.js": "~6.3.7",
@@ -287,7 +287,9 @@ export const MermaidExtension = CodeBlockLowlight.extend<MermaidOptions>({
stopEvent: (event) => {
if (event instanceof DragEvent && !nodeState.folded) {
event.preventDefault()
return true
}
return false
},
update: (node, decorations) => {
if (node.type.name !== MermaidExtension.name) return false
@@ -22,14 +22,15 @@ import {
type NodeViewRendererOptions,
type NodeViewRendererProps
} from '@tiptap/core'
import type { Node as ProseMirrorNode } from '@tiptap/pm/model'
import type { ComponentType, SvelteComponent } from 'svelte'
import { type Node } from '@tiptap/pm/model'
import { type Decoration, DecorationSet } from '@tiptap/pm/view'
import { createNodeViewContext } from './context'
import { SvelteRenderer } from './svelte-renderer'
export interface SvelteNodeViewRendererOptions extends NodeViewRendererOptions {
update?: (node: ProseMirrorNode, decorations: DecorationWithType[]) => boolean
update?: (node: Node, decorations: readonly Decoration[]) => boolean
contentAs?: string
contentClass?: string
componentProps?: Record<string, any>
@@ -59,7 +60,7 @@ class SvelteNodeView extends NodeView<SvelteNodeViewComponent, Editor, SvelteNod
const props: SvelteNodeViewProps = {
editor: this.editor,
node: this.node,
decorations: this.decorations,
decorations: this.decorations as readonly DecorationWithType[],
selected: false,
extension: this.extension,
getPos: () => this.getPos(),
@@ -69,6 +70,9 @@ class SvelteNodeView extends NodeView<SvelteNodeViewComponent, Editor, SvelteNod
deleteNode: () => {
this.deleteNode()
},
innerDecorations: DecorationSet.empty,
HTMLAttributes: {},
view: this.editor.view,
...(this.options.componentProps ?? {})
}
@@ -117,7 +121,7 @@ class SvelteNodeView extends NodeView<SvelteNodeViewComponent, Editor, SvelteNod
return false
}
update (node: ProseMirrorNode, decorations: DecorationWithType[]): boolean {
update (node: Node, decorations: readonly Decoration[]): boolean {
if (typeof this.options.update === 'function') {
return this.options.update(node, decorations)
}
@@ -35,8 +35,8 @@ export class CloudCollabProvider extends WebsocketProvider implements Provider {
super(url, encodeURIComponent(name), document, { params })
this.loaded = new Promise((resolve) => {
this.on('synced', resolve)
this.loaded = new Promise<any>((resolve) => {
this.on('sync', resolve)
})
}
+2 -2
View File
@@ -43,7 +43,7 @@
"@hcengineering/platform": "^0.6.11",
"@hcengineering/core": "^0.6.32",
"@hcengineering/ui": "^0.6.15",
"@tiptap/core": "^2.6.6",
"@tiptap/pm": "^2.6.6"
"@tiptap/core": "^2.11.3",
"@tiptap/pm": "^2.11.3"
}
}
+3 -3
View File
@@ -64,9 +64,9 @@
"@hcengineering/text-editor-resources": "^0.6.0",
"@hcengineering/time": "^0.6.0",
"@hcengineering/rank": "^0.6.4",
"@tiptap/extension-task-item": "^2.6.6",
"@tiptap/extension-task-list": "^2.6.6",
"fast-equals": "^5.0.1",
"@tiptap/extension-task-item": "^2.11.3",
"@tiptap/extension-task-list": "^2.11.3",
"fast-equals": "^5.2.2",
"@hcengineering/activity": "^0.6.0",
"@hcengineering/activity-resources": "^0.6.1",
"@hcengineering/workbench-resources": "^0.6.1"
+1 -1
View File
@@ -72,7 +72,7 @@
"@hcengineering/view-resources": "^0.6.0",
"@hcengineering/workbench": "^0.6.16",
"@hcengineering/workbench-resources": "^0.6.1",
"fast-equals": "^5.0.1",
"fast-equals": "^5.2.2",
"svelte": "^4.2.19"
}
}
+1 -1
View File
@@ -56,7 +56,7 @@
"@hcengineering/questions": "^0.1.0",
"@hcengineering/questions-resources": "^0.1.0",
"@hcengineering/training": "^0.1.0",
"fast-equals": "^5.0.1",
"fast-equals": "^5.2.2",
"lexorank": "~1.0.4",
"svelte": "^4.2.19"
}
+2 -2
View File
@@ -58,7 +58,7 @@
"@hcengineering/text-editor-resources": "^0.6.0",
"@hcengineering/analytics": "^0.6.0",
"@hcengineering/query": "^0.6.12",
"fast-equals": "^5.0.1",
"hls.js": "^1.5.15"
"fast-equals": "^5.2.2",
"hls.js": "^1.5.20"
}
}
@@ -23,22 +23,15 @@
Space,
mergeQueries
} from '@hcengineering/core'
import { IntlString, getResource } from '@hcengineering/platform'
import { IntlString } from '@hcengineering/platform'
import { createQuery, getClient, reduceCalls } from '@hcengineering/presentation'
import { AnyComponent, AnySvelteComponent } from '@hcengineering/ui'
import {
BuildModelKey,
ViewOptionModel,
ViewOptions,
ViewOptionsOption,
ViewQueryOption,
Viewlet
} from '@hcengineering/view'
import { BuildModelKey, ViewOptionModel, ViewOptions, Viewlet } from '@hcengineering/view'
import { createEventDispatcher } from 'svelte'
import { SelectionFocusProvider } from '../../selection'
import { buildConfigLookup } from '../../utils'
import ListCategories from './ListCategories.svelte'
import { getResultOptions, getResultQuery } from '../../viewOptions'
import ListCategories from './ListCategories.svelte'
export let _class: Ref<Class<Doc>>
export let space: Ref<Space> | undefined = undefined
@@ -230,6 +230,7 @@
min-height: 2.75rem;
min-width: 0;
background: var(--theme-bg-color);
border-radius: 0.25rem 0.25rem 0 0;
.on-hover {
visibility: hidden;
@@ -268,7 +269,7 @@
/* Global styles in components.scss and there is an influence from the Scroller component */
&.collapsed {
border-radius: 0 0 0.25rem 0.25rem;
border-radius: 0.25rem;
.chevron {
transform: rotate(0deg);
+1 -1
View File
@@ -57,7 +57,7 @@
"@hcengineering/support": "^0.6.5",
"@hcengineering/support-resources": "^0.6.0",
"@hcengineering/view-resources": "^0.6.0",
"fast-copy": "~3.0.1",
"fast-copy": "^3.0.2",
"@hcengineering/analytics": "^0.6.0"
}
}

Some files were not shown because too many files have changed in this diff Show More