Move onboard into separate app (#6191)

Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
Alexander Onnikov
2024-07-31 15:14:21 +07:00
committed by GitHub
parent febefacef9
commit b6af63fcb0
69 changed files with 1682 additions and 34 deletions
+119 -2
View File
@@ -548,6 +548,15 @@ dependencies:
'@rush-temp/notification-resources':
specifier: file:./projects/notification-resources.tgz
version: file:projects/notification-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2)
'@rush-temp/onboard':
specifier: file:./projects/onboard.tgz
version: file:projects/onboard.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2)
'@rush-temp/onboard-assets':
specifier: file:./projects/onboard-assets.tgz
version: file:projects/onboard-assets.tgz(esbuild@0.20.1)(ts-node@10.9.2)
'@rush-temp/onboard-resources':
specifier: file:./projects/onboard-resources.tgz
version: file:projects/onboard-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(file-loader@6.2.0)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2)(webpack@5.90.3)
'@rush-temp/openai':
specifier: file:./projects/openai.tgz
version: file:projects/openai.tgz(esbuild@0.20.1)(ts-node@10.9.2)
@@ -25154,7 +25163,7 @@ packages:
dev: false
file:projects/desktop.tgz(bufferutil@4.0.8)(sass@1.71.1)(utf-8-validate@6.0.4):
resolution: {integrity: sha512-ttUpcNZ8VKqQjGhZ6u9UwTTCOpXBKD8BC7CziczYCzKpTCjdwEJh9agnt8uJU6eETt57hJY2c8BU/aIESyYMZA==, tarball: file:projects/desktop.tgz}
resolution: {integrity: sha512-Zqlhi9UprYvF9L0ANw8sSkxTuqMYWTDmIUV6wYDTHd+NMt8NKshXy9J39mll8dW0h93TeY538RePwutrdvM4kA==, tarball: file:projects/desktop.tgz}
id: file:projects/desktop.tgz
name: '@rush-temp/desktop'
version: 0.0.0
@@ -28371,6 +28380,114 @@ packages:
- ts-node
dev: false
file:projects/onboard-assets.tgz(esbuild@0.20.1)(ts-node@10.9.2):
resolution: {integrity: sha512-W5Ci7+hkutP59Tt2Gp+5tElU0Ix6cptU7EQAYxwnuPQD+EPL51V5XzfIz1Kpf2mnicgb/TB2MFDX+uBjY5hq1A==, tarball: file:projects/onboard-assets.tgz}
id: file:projects/onboard-assets.tgz
name: '@rush-temp/onboard-assets'
version: 0.0.0
dependencies:
'@types/jest': 29.5.12
'@types/node': 20.11.19
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
eslint: 8.56.0
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3)
eslint-plugin-import: 2.29.1(eslint@8.56.0)
eslint-plugin-n: 15.7.0(eslint@8.56.0)
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2)
prettier: 3.2.5
ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3)
typescript: 5.3.3
transitivePeerDependencies:
- '@babel/core'
- '@jest/types'
- babel-jest
- babel-plugin-macros
- esbuild
- node-notifier
- supports-color
- ts-node
dev: false
file:projects/onboard-resources.tgz(@types/node@20.11.19)(esbuild@0.20.1)(file-loader@6.2.0)(postcss-load-config@4.0.2)(postcss@8.4.35)(ts-node@10.9.2)(webpack@5.90.3):
resolution: {integrity: sha512-3Gt9PEcA7T44mLBBC0KwwZQ6Qs7pXYbqQaagWFjfT+4MTvR9XVk4L+67U0yFYqpS/EeODSc+dJklk3QBY2O36g==, tarball: file:projects/onboard-resources.tgz}
id: file:projects/onboard-resources.tgz
name: '@rush-temp/onboard-resources'
version: 0.0.0
dependencies:
'@types/jest': 29.5.12
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
eslint: 8.56.0
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3)
eslint-plugin-import: 2.29.1(eslint@8.56.0)
eslint-plugin-n: 15.7.0(eslint@8.56.0)
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
eslint-plugin-svelte: 2.35.1(eslint@8.56.0)(svelte@4.2.12)(ts-node@10.9.2)
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2)
prettier: 3.2.5
prettier-plugin-svelte: 3.2.2(prettier@3.2.5)(svelte@4.2.12)
sass: 1.71.1
svelte: 4.2.12
svelte-check: 3.6.9(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.12)
svelte-eslint-parser: 0.33.1(svelte@4.2.12)
svelte-loader: 3.2.0(svelte@4.2.12)
svelte-preprocess: 5.1.3(postcss-load-config@4.0.2)(postcss@8.4.35)(sass@1.71.1)(svelte@4.2.12)(typescript@5.3.3)
ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3)
typescript: 5.3.3
url-loader: 4.1.1(file-loader@6.2.0)(webpack@5.90.3)
transitivePeerDependencies:
- '@babel/core'
- '@jest/types'
- '@types/node'
- babel-jest
- babel-plugin-macros
- coffeescript
- esbuild
- file-loader
- less
- node-notifier
- postcss
- postcss-load-config
- pug
- stylus
- sugarss
- supports-color
- ts-node
- webpack
dev: false
file:projects/onboard.tgz(@types/node@20.11.19)(esbuild@0.20.1)(ts-node@10.9.2):
resolution: {integrity: sha512-9KGKUTn0xaR5BXCSR0PDirFhStjot+g2QvBO1FDlehb2HWedfln6oswEUtM5n6VJwKS0UUKDwhpm5wLPo7KUMg==, tarball: file:projects/onboard.tgz}
id: file:projects/onboard.tgz
name: '@rush-temp/onboard'
version: 0.0.0
dependencies:
'@types/jest': 29.5.12
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3)
eslint: 8.56.0
eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3)
eslint-plugin-import: 2.29.1(eslint@8.56.0)
eslint-plugin-n: 15.7.0(eslint@8.56.0)
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2)
prettier: 3.2.5
ts-jest: 29.1.2(esbuild@0.20.1)(jest@29.7.0)(typescript@5.3.3)
typescript: 5.3.3
transitivePeerDependencies:
- '@babel/core'
- '@jest/types'
- '@types/node'
- babel-jest
- babel-plugin-macros
- esbuild
- node-notifier
- supports-color
- ts-node
dev: false
file:projects/openai.tgz(esbuild@0.20.1)(ts-node@10.9.2):
resolution: {integrity: sha512-FTxDecAefUxIdNRC3gHwqZrNJpHI6Mi/fDduPFKm4YCxy3jpzFgWTKeRRpIQ2AZTQ6Cw4cWANh0VAI2DyTo8Ig==, tarball: file:projects/openai.tgz}
id: file:projects/openai.tgz
@@ -29436,7 +29553,7 @@ packages:
dev: false
file:projects/prod.tgz(bufferutil@4.0.8)(sass@1.71.1)(ts-node@10.9.2)(utf-8-validate@6.0.4):
resolution: {integrity: sha512-nTK4PWGg4h8wB67Ax8fO8iqUg1fOr8JhCgNTuz2o6VqTFnk7cDEc2k9FuYkdSho6HelRqEFcKGkmXsZEAungXQ==, tarball: file:projects/prod.tgz}
resolution: {integrity: sha512-U0i5q0YgmqM+29OoNKE5F7SYHLenVyjGDEus4MgN7CqMIC1n6n2gtztzRm2O6/j8hba5E6asgHgGF+ljESlbGw==, tarball: file:projects/prod.tgz}
id: file:projects/prod.tgz
name: '@rush-temp/prod'
version: 0.0.0
+3
View File
@@ -69,6 +69,9 @@
"@hcengineering/login": "^0.6.12",
"@hcengineering/login-assets": "^0.6.0",
"@hcengineering/login-resources": "^0.6.2",
"@hcengineering/onboard": "^0.6.0",
"@hcengineering/onboard-assets": "^0.6.0",
"@hcengineering/onboard-resources": "^0.6.0",
"@hcengineering/client": "^0.6.18",
"@hcengineering/workbench": "^0.6.16",
"@hcengineering/workbench-resources": "^0.6.1",
+4
View File
@@ -28,6 +28,7 @@ import { inventoryId } from '@hcengineering/inventory'
import { leadId } from '@hcengineering/lead'
import login, { loginId } from '@hcengineering/login'
import notification, { notificationId } from '@hcengineering/notification'
import onboard, { onboardId } from '@hcengineering/onboard'
import { recruitId } from '@hcengineering/recruit'
import rekoni from '@hcengineering/rekoni'
import { requestId } from '@hcengineering/request'
@@ -143,6 +144,7 @@ function configureI18n (): void {
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`)
@@ -240,6 +242,7 @@ export async function configurePlatform (): Promise<void> {
new Map([
[workbenchId, workbench.component.WorkbenchApp],
[loginId, login.component.LoginApp],
[onboardId, onboard.component.OnboardApp],
[calendarId, calendar.component.ConnectApp],
[guestId, guest.component.GuestApp]
])
@@ -250,6 +253,7 @@ export async function configurePlatform (): Promise<void> {
addLocation(clientId, async () => await import('@hcengineering/client-resources'))
addLocation(loginId, async () => await import('@hcengineering/login-resources'))
addLocation(onboardId, async () => await import('@hcengineering/onboard-resources'))
addLocation(workbenchId, async () => await import('@hcengineering/workbench-resources'))
addLocation(viewId, async () => await import('@hcengineering/view-resources'))
addLocation(taskId, async () => await import('@hcengineering/task-resources'))
+3
View File
@@ -59,6 +59,9 @@
"@hcengineering/login": "^0.6.12",
"@hcengineering/login-assets": "^0.6.0",
"@hcengineering/login-resources": "^0.6.2",
"@hcengineering/onboard": "^0.6.0",
"@hcengineering/onboard-assets": "^0.6.0",
"@hcengineering/onboard-resources": "^0.6.0",
"@hcengineering/client": "^0.6.18",
"@hcengineering/workbench": "^0.6.16",
"@hcengineering/workbench-resources": "^0.6.1",
+4
View File
@@ -32,6 +32,7 @@ import { inventoryId } from '@hcengineering/inventory'
import { leadId } from '@hcengineering/lead'
import login, { loginId } from '@hcengineering/login'
import notification, { notificationId } from '@hcengineering/notification'
import onboard, { onboardId } from '@hcengineering/onboard'
import { recruitId } from '@hcengineering/recruit'
import rekoni from '@hcengineering/rekoni'
import { requestId } from '@hcengineering/request'
@@ -192,6 +193,7 @@ function configureI18n(): void {
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(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(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`))
@@ -326,6 +328,7 @@ export async function configurePlatform() {
new Map([
[workbenchId, workbench.component.WorkbenchApp],
[loginId, login.component.LoginApp],
[onboardId, onboard.component.OnboardApp],
[githubId, github.component.ConnectApp],
[calendarId, calendar.component.ConnectApp],
[guestId, guest.component.GuestApp]
@@ -337,6 +340,7 @@ export async function configurePlatform() {
addLocation(clientId, () => import(/* webpackChunkName: "client" */ '@hcengineering/client-resources'))
addLocation(loginId, () => import(/* webpackChunkName: "login" */ '@hcengineering/login-resources'))
addLocation(onboardId, () => import(/* webpackChunkName: "onboard" */ '@hcengineering/onboard-resources'))
addLocation(workbenchId, () => import(/* webpackChunkName: "workbench" */ '@hcengineering/workbench-resources'))
addLocation(viewId, () => import(/* webpackChunkName: "view" */ '@hcengineering/view-resources'))
addLocation(taskId, () => import(/* webpackChunkName: "task" */ '@hcengineering/task-resources'))
@@ -18,7 +18,7 @@
setMetadataLocalStorage(login.metadata.LastToken, result.token)
setMetadataLocalStorage(login.metadata.LoginEndpoint, result.endpoint)
setMetadataLocalStorage(login.metadata.LoginEmail, result.email)
await afterConfirm('onboard')
await afterConfirm()
} else {
goTo('login')
}
@@ -51,7 +51,6 @@
import loginBackWebp from '../../img/login_back.webp'
import loginBack2xWebp from '../../img/login_back_2x.webp'
import login from '../plugin'
import OnboardForm from './OnboardForm.svelte'
export let page: Pages = 'signup'
@@ -138,8 +137,6 @@
<LoginForm {navigateUrl} />
{:else if page === 'signup'}
<SignupForm />
{:else if page === 'onboard'}
<OnboardForm />
{:else if page === 'createWorkspace'}
<CreateWorkspaceForm />
{:else if page === 'password'}
@@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import { LoginInfo, WorkspaceLoginInfo } from '@hcengineering/login'
import login, { LoginInfo, WorkspaceLoginInfo } from '@hcengineering/login'
import { OK } from '@hcengineering/platform'
import { onMount } from 'svelte'
+1 -8
View File
@@ -62,16 +62,9 @@ export const pages = [
'confirm',
'confirmationSend',
'auth',
'login-password',
'onboard'
'login-password'
] as const
export enum OnboardSteps {
Workspace = 'workspace',
User = 'user',
Finish = 'finish'
}
export enum OtpLoginSteps {
Email = 'email',
Otp = 'otp'
+1 -7
View File
@@ -67,12 +67,6 @@ export default mergeIds(loginId, login, {
SentTo: '' as IntlString,
CanFindCode: '' as IntlString,
LoginWithCode: '' as IntlString,
LoginWithPassword: '' as IntlString,
FillInProfile: '' as IntlString,
SetUpPassword: '' as IntlString,
Next: '' as IntlString,
Skip: '' as IntlString,
SignUpCompleted: '' as IntlString,
StartUsingHuly: '' as IntlString
LoginWithPassword: '' as IntlString
}
})
+2 -11
View File
@@ -872,10 +872,10 @@ export function getHref (path: Pages): string {
return host + url
}
export async function afterConfirm (wspage: 'onboard' | 'createWorkspace' = 'createWorkspace'): Promise<void> {
export async function afterConfirm (): Promise<void> {
const joinedWS = await getWorkspaces()
if (joinedWS.length === 0) {
goTo(wspage)
goTo('createWorkspace')
} else if (joinedWS.length === 1) {
const result = (await selectWorkspace(joinedWS[0].workspace, null))[1]
if (result !== undefined) {
@@ -1044,12 +1044,3 @@ export async function doLoginNavigate (
navigate(loc)
}
}
export async function ensureConfirmed (account: LoginInfo): Promise<void> {
if (!account.confirmed) {
const loc = getCurrentLocation()
loc.path[1] = 'confirmationSend'
loc.path.length = 2
navigate(loc)
}
}
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
extends: ['./node_modules/@hcengineering/platform-rig/profiles/assets/eslint.config.json'],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
}
}
+17
View File
@@ -0,0 +1,17 @@
{
"name": "@hcengineering/login-assets",
"entries": [
{
"version": "0.6.0",
"tag": "@hcengineering/login-assets_v0.6.0",
"date": "Sun, 08 Aug 2021 10:14:57 GMT",
"comments": {
"dependency": [
{
"comment": "Updating dependency \"@hcengineering/platform\" from `~0.6.3` to `~0.6.4`"
}
]
}
}
]
}
+9
View File
@@ -0,0 +1,9 @@
# Change Log - @hcengineering/login-assets
This log was last generated on Sun, 08 Aug 2021 10:14:57 GMT and should not be manually modified.
## 0.6.0
Sun, 08 Aug 2021 10:14:57 GMT
_Initial release_
+5
View File
@@ -0,0 +1,5 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig",
"rigProfile": "assets"
}
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
roots: ["./src"],
coverageReporters: ["text-summary", "html"]
}
+19
View File
@@ -0,0 +1,19 @@
{
"status": {
"RequiredField": "Required field {field}",
"FieldsDoNotMatch": "{field} don't match {field2}",
"ConnectingToServer": "Connecting to server...."
},
"string": {
"CreateWorkspace": "Create workspace",
"LastName": "Last name",
"FirstName": "First name",
"Workspace": "Workspace name",
"FillInProfile": "Fill in your profile",
"SetUpPassword": "Set up your password",
"Next": "Next",
"Skip": "Skip",
"SignUpCompleted": "Sign up completed",
"StartUsingHuly": "Start using Huly"
}
}
+19
View File
@@ -0,0 +1,19 @@
{
"status": {
"RequiredField": "Campo obligatorio: {field}",
"FieldsDoNotMatch": "{field} no coincide con {field2}",
"ConnectingToServer": "Conectando al servidor..."
},
"string": {
"CreateWorkspace": "Crear espacio de trabajo",
"LastName": "Apellido",
"FirstName": "Nombre",
"Workspace": "Nombre del espacio de trabajo",
"FillInProfile": "Rellena tu perfil",
"SetUpPassword": "Establecer tu contraseña",
"Next": "Siguiente",
"Skip": "Saltar",
"SignUpCompleted": "Registro completado",
"StartUsingHuly": "Comienza a usar Huly"
}
}
+19
View File
@@ -0,0 +1,19 @@
{
"status": {
"RequiredField": "Champ requis {field}",
"FieldsDoNotMatch": "{field} ne correspond pas à {field2}",
"ConnectingToServer": "Connexion au serveur..."
},
"string": {
"CreateWorkspace": "Créer un espace de travail",
"LastName": "Nom de famille",
"FirstName": "Prénom",
"Workspace": "Nom de l'espace de travail",
"FillInProfile": "Remplissez votre profil",
"SetUpPassword": "Définir votre mot de passe",
"Next": "Suivant",
"Skip": "Passer",
"SignUpCompleted": "Inscription terminée",
"StartUsingHuly": "Commencez à utiliser Huly"
}
}
+19
View File
@@ -0,0 +1,19 @@
{
"status": {
"RequiredField": "Campo obrigatório {field}",
"FieldsDoNotMatch": "{field} não coincidem com {field2}",
"ConnectingToServer": "A ligar ao servidor...."
},
"string": {
"CreateWorkspace": "Criar espaço de trabalho",
"LastName": "Apelido",
"FirstName": "Nome",
"Workspace": "Nome do espaço de trabalho",
"FillInProfile": "Preencha o seu perfil",
"SetUpPassword": "Definir a sua palavra-passe",
"Next": "Seguinte",
"Skip": "Saltar",
"SignUpCompleted": "Registo concluído",
"StartUsingHuly": "Começar a usar Huly"
}
}
+19
View File
@@ -0,0 +1,19 @@
{
"status": {
"RequiredField": "Требуется заполнить {field}",
"FieldsDoNotMatch": "{field} не совпадает {field2}",
"ConnectingToServer": "Подключение к серверу...."
},
"string": {
"CreateWorkspace": "Создать рабочее пространство",
"LastName": "Фамилия",
"FirstName": "Имя",
"Workspace": "Рабочее пространство",
"FillInProfile": "Заполните профиль",
"SetUpPassword": "Установите пароль",
"Next": "Дальше",
"Skip": "Пропустить",
"SignUpCompleted": "Регистрация завершена",
"StartUsingHuly": "Начать использовать Huly"
}
}
+19
View File
@@ -0,0 +1,19 @@
{
"status": {
"RequiredField": "必填字段 {field}",
"FieldsDoNotMatch": "{field} 与 {field2} 不匹配",
"ConnectingToServer": "正在连接服务器...."
},
"string": {
"CreateWorkspace": "创建工作区",
"LastName": "姓",
"FirstName": "名",
"Workspace": "工作区名称",
"FillInProfile": "填写您的个人资料",
"SetUpPassword": "设置您的密码",
"Next": "下一个",
"Skip": "跳过",
"SignUpCompleted": "注册完成",
"StartUsingHuly": "开始使用 Huly"
}
}
+39
View File
@@ -0,0 +1,39 @@
{
"name": "@hcengineering/onboard-assets",
"version": "0.6.0",
"main": "src/index.ts",
"author": "Hardcore Engineering Inc.",
"template": "@hcengineering/assets-package",
"license": "EPL-2.0",
"scripts": {
"build": "compile",
"test": "jest --passWithNoTests --silent",
"build:docs": "",
"format": "format src",
"build:watch": "compile",
"_phase:build": "compile transpile src",
"_phase:test": "jest --passWithNoTests --silent",
"_phase:format": "format src",
"_phase:validate": "compile validate"
},
"devDependencies": {
"@hcengineering/platform-rig": "^0.6.0",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
"eslint-config-standard-with-typescript": "^40.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.4.0",
"eslint-plugin-promise": "^6.1.1",
"eslint": "^8.54.0",
"prettier": "^3.1.0",
"@types/node": "~20.11.16",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.5",
"typescript": "^5.3.3"
},
"dependencies": {
"@hcengineering/platform": "^0.6.11",
"@hcengineering/onboard": "^0.6.0"
}
}
@@ -0,0 +1,6 @@
import { makeLocalesTest } from '@hcengineering/platform'
it(
'Locales are equale',
makeLocalesTest((lang) => import(`../../lang/${lang}.json`))
)
+14
View File
@@ -0,0 +1,14 @@
//
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
+11
View File
@@ -0,0 +1,11 @@
{
"extends": "./node_modules/@hcengineering/platform-rig/profiles/assets/tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"declarationDir": "./types",
"types": ["node", "jest"],
"tsBuildInfoFile": ".build/build.tsbuildinfo"
}
}
+4
View File
@@ -0,0 +1,4 @@
module.exports = {
extends: ['./node_modules/@hcengineering/platform-rig/profiles/ui/eslint.config.json'],
parserOptions: { tsconfigRootDir: __dirname }
}
+22
View File
@@ -0,0 +1,22 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"trailingComma": "none",
"tabWidth": 2,
"semi": false,
"singleQuote": true,
"printWidth": 120,
"useTabs": false,
"bracketSpacing": true,
"proseWrap": "preserve",
"plugins": [
"prettier-plugin-svelte"
],
"overrides": [
{
"files": "*.svelte",
"options": {
"parser": "svelte"
}
}
]
}
+49
View File
@@ -0,0 +1,49 @@
{
"name": "@hcengineering/login-resources",
"entries": [
{
"version": "0.6.2",
"tag": "@hcengineering/login-resources_v0.6.2",
"date": "Mon, 09 Aug 2021 08:00:44 GMT",
"comments": {
"patch": [
{
"comment": "fix"
}
]
}
},
{
"version": "0.6.1",
"tag": "@hcengineering/login-resources_v0.6.1",
"date": "Sun, 08 Aug 2021 21:05:26 GMT",
"comments": {
"patch": [
{
"comment": "Fix server connection"
}
],
"dependency": [
{
"comment": "Updating dependency \"@hcengineering/login\" from `~0.6.0` to `~0.6.1`"
},
{
"comment": "Updating dependency \"@hcengineering/workbench\" from `~0.6.0` to `~0.6.1`"
}
]
}
},
{
"version": "0.6.0",
"tag": "@hcengineering/login-resources_v0.6.0",
"date": "Sun, 08 Aug 2021 10:14:57 GMT",
"comments": {
"dependency": [
{
"comment": "Updating dependency \"@hcengineering/platform\" from `~0.6.0` to `~0.6.4`"
}
]
}
}
]
}
+23
View File
@@ -0,0 +1,23 @@
# Change Log - @hcengineering/login-resources
This log was last generated on Mon, 09 Aug 2021 08:00:44 GMT and should not be manually modified.
## 0.6.2
Mon, 09 Aug 2021 08:00:44 GMT
### Patches
- fix
## 0.6.1
Sun, 08 Aug 2021 21:05:26 GMT
### Patches
- Fix server connection
## 0.6.0
Sun, 08 Aug 2021 10:14:57 GMT
_Initial release_
@@ -0,0 +1,5 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig",
"rigProfile": "ui"
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1020 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 763 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

@@ -0,0 +1,3 @@
<svg width="63" height="79" viewBox="0 0 63 79" fill="#fff" xmlns="http://www.w3.org/2000/svg">
<path d="M34.8089 0.679932H0.378906V78.9999H15.2289V51.6099H34.8089C52.8489 51.6099 62.5289 41.2699 62.5289 26.1999C62.5289 11.0199 52.8489 0.679932 34.8089 0.679932ZM34.1489 38.8499H15.2289V13.4399H34.1489C42.8389 13.4399 47.5689 18.0599 47.5689 26.1999C47.5689 34.3399 42.8389 38.8499 34.1489 38.8499Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 423 B

@@ -0,0 +1,3 @@
<svg width="63" height="79" viewBox="0 0 63 79" fill="#fff" xmlns="http://www.w3.org/2000/svg">
<path d="M34.8089 0.679932H0.378906V78.9999H15.2289V51.6099H34.8089C52.8489 51.6099 62.5289 41.2699 62.5289 26.1999C62.5289 11.0199 52.8489 0.679932 34.8089 0.679932ZM34.1489 38.8499H15.2289V13.4399H34.1489C42.8389 13.4399 47.5689 18.0599 47.5689 26.1999C47.5689 34.3399 42.8389 38.8499 34.1489 38.8499Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 423 B

+5
View File
@@ -0,0 +1,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)']
}
+55
View File
@@ -0,0 +1,55 @@
{
"name": "@hcengineering/onboard-resources",
"version": "0.6.0",
"main": "src/index.ts",
"author": "Hardcore Engineering Inc.",
"license": "EPL-2.0",
"scripts": {
"build": "compile ui",
"test": "jest --passWithNoTests --silent",
"build:docs": "api-extractor run --local",
"format": "format src",
"build:watch": "compile ui",
"_phase:build": "compile ui",
"_phase:test": "jest --passWithNoTests --silent",
"_phase:format": "format src",
"_phase:validate": "compile validate"
},
"devDependencies": {
"svelte-loader": "^3.2.0",
"sass": "^1.53.0",
"svelte-preprocess": "^5.1.3",
"@hcengineering/platform-rig": "^0.6.0",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
"eslint-config-standard-with-typescript": "^40.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.4.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-svelte": "^2.35.1",
"prettier-plugin-svelte": "^3.2.2",
"eslint": "^8.54.0",
"prettier": "^3.1.0",
"svelte-check": "^3.6.9",
"typescript": "^5.3.3",
"url-loader": "~4.1.1",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.5",
"svelte-eslint-parser": "^0.33.1"
},
"dependencies": {
"@hcengineering/platform": "^0.6.11",
"svelte": "^4.2.12",
"@hcengineering/login": "^0.6.12",
"@hcengineering/login-resources": "^0.6.2",
"@hcengineering/onboard": "^0.6.0",
"@hcengineering/ui": "^0.6.15",
"@hcengineering/workbench": "^0.6.16",
"@hcengineering/core": "^0.6.32",
"@hcengineering/presentation": "^0.6.3",
"@hcengineering/setting": "^0.6.17",
"@hcengineering/theme": "^0.6.5",
"@hcengineering/analytics": "^0.6.0"
}
}
@@ -0,0 +1,5 @@
module.exports = {
plugins: [
require('autoprefixer')
]
}
@@ -0,0 +1,32 @@
<script lang="ts">
import login, { LoginInfo, WorkspaceLoginInfo } from '@hcengineering/login'
import { getSessionLoginInfo, navigateToWorkspace } from '@hcengineering/login-resources'
import { setMetadata } from '@hcengineering/platform'
import presentation from '@hcengineering/presentation'
import { Loading, setMetadataLocalStorage } from '@hcengineering/ui'
import { onMount } from 'svelte'
import { afterConfirm, goToLogin } from '../utils'
onMount(async () => {
const result = await getSessionLoginInfo()
if (result !== undefined) {
if (isWorkspaceLoginInfo(result)) {
navigateToWorkspace(result.workspace, result)
return
}
setMetadata(presentation.metadata.Token, result.token)
setMetadataLocalStorage(login.metadata.LastToken, result.token)
setMetadataLocalStorage(login.metadata.LoginEndpoint, result.endpoint)
setMetadataLocalStorage(login.metadata.LoginEmail, result.email)
await afterConfirm()
} else {
goToLogin('login')
}
})
function isWorkspaceLoginInfo (info: WorkspaceLoginInfo | LoginInfo): info is WorkspaceLoginInfo {
return (info as WorkspaceLoginInfo).workspace !== undefined
}
</script>
<Loading />
@@ -0,0 +1,45 @@
<!--
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { Label } from '@hcengineering/ui'
import { NavLink } from '@hcengineering/presentation'
import { BottomAction } from '../index'
import { getHref } from '../utils'
export let action: BottomAction
</script>
<div>
{#if action.caption}
<span><Label label={action.caption} /></span>
{/if}
{#if action.page}
<NavLink href={getHref(action.page)}><Label label={action.i18n} /></NavLink>
{:else}
<a href="." on:click|preventDefault={action.func}><Label label={action.i18n} /></a>
{/if}
</div>
<style lang="scss">
span {
color: var(--theme-darker-color);
}
a {
font-weight: 400;
color: var(--theme-content-color);
}
</style>
@@ -0,0 +1,258 @@
<!--
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { reduceCalls } from '@hcengineering/core'
import { type IntlString, OK, Severity, Status, translate } from '@hcengineering/platform'
import { Button, Label, StylishEdit, deviceOptionsStore as deviceInfo, themeStore } from '@hcengineering/ui'
import { onMount } from 'svelte'
import { BottomAction } from '..'
import onboard from '../plugin'
import BottomActionComponent from './BottomAction.svelte'
import StatusControl from './StatusControl.svelte'
interface Field {
id?: string
name: string
i18n: IntlString
password?: boolean
optional?: boolean
short?: boolean
rules?: {
rule: RegExp
notMatch: boolean
ruleDescr: IntlString
}[]
}
interface Action {
i18n: IntlString
func: () => Promise<void>
}
export let caption: IntlString
export let status: Status
export let fields: Field[]
export let action: Action
export let secondaryButtonLabel: IntlString | undefined = undefined
export let secondaryButtonAction: (() => void) | undefined = undefined
export let bottomActions: BottomAction[] = []
export let object: any
export let ignoreInitialValidation: boolean = false
export let subtitle: string | undefined = undefined
let valid: boolean = false
async function validateAsync (language: string): Promise<boolean> {
if (ignoreInitialValidation) return true
for (const field of fields) {
const v = object[field.name]
const f = field
if (!f.optional && (!v || v.trim() === '')) {
status = new Status(Severity.INFO, onboard.status.RequiredField, {
field: await translate(field.i18n, {}, language)
})
return false
}
if (f.id !== undefined) {
const sameFields = fields.filter((f) => f.id === field.id)
for (const field of sameFields) {
const v = object[field.name]
if (v !== object[f.name]) {
status = new Status(Severity.INFO, onboard.status.FieldsDoNotMatch, {
field: await translate(field.i18n, {}, language),
field2: await translate(f.i18n, {}, language)
})
return false
}
}
}
if (f.rules !== undefined) {
for (const rule of f.rules) {
if (rule.rule.test(v) === rule.notMatch) {
status = new Status(Severity.INFO, rule.ruleDescr, {})
return false
}
}
}
}
status = OK
return true
}
const validate = reduceCalls(async (language: string) => {
valid = await validateAsync(language)
})
$: void validate($themeStore.language)
let inAction = false
function performAction (action: Action): void {
for (const field of fields) {
trim(field.name)
}
inAction = true
void action.func().finally(() => {
inAction = false
})
}
onMount(() => (ignoreInitialValidation = false))
function trim (field: string): void {
object[field] = (object[field] as string).trim()
}
</script>
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<form
class="container"
style:padding={$deviceInfo.docWidth <= 480 ? '.25rem 1.25rem' : '4rem 5rem'}
style:min-height={$deviceInfo.docHeight > 720 ? '42rem' : '0'}
on:keydown={(evt) => {
if (evt.key === 'Enter') {
evt.preventDefault()
evt.stopPropagation()
if (!inAction) {
void validate($themeStore.language).then((res) => {
if (res) {
performAction(action)
}
})
}
}
}}
>
{#if subtitle !== undefined}
<div class="fs-title">
{subtitle}
</div>
{/if}
<div class="title"><Label label={caption} /></div>
<div class="form">
{#each fields as field (field.name)}
<div class={field.short && !($deviceInfo.docWidth <= 600) ? 'form-col' : 'form-row'}>
<StylishEdit
label={field.i18n}
name={field.id}
password={field.password}
bind:value={object[field.name]}
on:input={() => validate($themeStore.language)}
on:blur={() => {
trim(field.name)
}}
/>
</div>
{/each}
<div class="status">
<StatusControl {status} />
</div>
<div class="form-row send">
<Button
label={action.i18n}
kind={'contrast'}
shape={'round2'}
size={'x-large'}
width="100%"
loading={inAction}
disabled={status.severity !== Severity.OK && status.severity !== Severity.ERROR}
on:click={(e) => {
e.preventDefault()
performAction(action)
}}
/>
</div>
{#if secondaryButtonLabel && secondaryButtonAction}
<div class="form-row">
<Button
label={secondaryButtonLabel}
width="100%"
on:click={(e) => {
e.preventDefault()
secondaryButtonAction?.()
}}
/>
</div>
{/if}
</div>
{#if bottomActions.length}
<div class="footer">
{#each bottomActions as action}
<BottomActionComponent {action} />
{/each}
</div>
{/if}
</form>
<style lang="scss">
.container {
overflow: hidden;
display: flex;
flex-direction: column;
.title {
font-weight: 500;
font-size: 1.25rem;
color: var(--theme-caption-color);
}
.status {
padding-top: 1rem;
grid-column-start: 1;
grid-column-end: 3;
}
.form {
display: grid;
grid-template-columns: 1fr 1fr;
column-gap: 0.75rem;
row-gap: 1.5rem;
margin-top: 1.5rem;
.form-row {
grid-column-start: 1;
grid-column-end: 3;
}
.hint {
margin-top: 1rem;
font-size: 0.8rem;
color: var(--theme-content-color);
}
.send {
margin-top: 0rem;
}
}
.grow-separator {
flex-grow: 1;
}
.footer {
margin-top: 1.75rem;
font-size: 0.8rem;
color: var(--theme-content-color);
span {
color: var(--theme-darker-color);
}
a {
font-weight: 500;
text-decoration: underline;
color: var(--theme-content-color);
}
}
}
</style>
@@ -0,0 +1,228 @@
<!--
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import login from '@hcengineering/login'
import { getAccount } from '@hcengineering/login-resources'
import { getMetadata, setMetadata } from '@hcengineering/platform'
import presentation from '@hcengineering/presentation'
import {
Location,
Popup,
Scroller,
deviceOptionsStore as deviceInfo,
fetchMetadataLocalStorage,
getCurrentLocation,
location,
setMetadataLocalStorage,
themeStore
} from '@hcengineering/ui'
import workbench from '@hcengineering/workbench'
import { onDestroy, onMount } from 'svelte'
import Auth from './Auth.svelte'
import LoginIcon from './icons/OnboardIcon.svelte'
import OnboardForm from './OnboardForm.svelte'
import loginBack from '../../img/login_back.png'
import loginBack2x from '../../img/login_back_2x.png'
import loginBackAvif from '../../img/login_back.avif'
import loginBack2xAvif from '../../img/login_back_2x.avif'
import loginBackWebp from '../../img/login_back.webp'
import loginBack2xWebp from '../../img/login_back_2x.webp'
import { Pages, pages } from '..'
export let page: Pages = 'onboard'
onDestroy(location.subscribe(updatePageLoc))
function updatePageLoc (loc: Location): void {
const token = getMetadata(presentation.metadata.Token)
page = (loc.path[1] as Pages) ?? (token != null ? 'selectWorkspace' : 'login')
const allowedUnauthPages: Pages[] = ['onboard', 'auth']
if (token === undefined ? !allowedUnauthPages.includes(page) : !pages.includes(page)) {
page = 'onboard'
}
}
async function chooseToken (): Promise<void> {
if (page === 'auth') {
// token handled by auth page
return
}
if (getMetadata(presentation.metadata.Token) == null) {
const lastToken = fetchMetadataLocalStorage(login.metadata.LastToken)
if (lastToken != null) {
try {
const info = await getAccount(false)
if (info !== undefined) {
setMetadata(presentation.metadata.Token, info.token)
setMetadataLocalStorage(login.metadata.LastToken, info.token)
setMetadataLocalStorage(login.metadata.LoginEndpoint, info.endpoint)
setMetadataLocalStorage(login.metadata.LoginEmail, info.email)
updatePageLoc(getCurrentLocation())
}
} catch (err: any) {
setMetadataLocalStorage(login.metadata.LastToken, null)
}
}
}
}
onMount(chooseToken)
</script>
<div class="theme-dark w-full h-full backd" class:paneld={$deviceInfo.docWidth <= 768} class:white={!$themeStore.dark}>
<div class="bg-image clear-mins" class:back={$deviceInfo.docWidth > 768} class:p-4={$deviceInfo.docWidth > 768}>
<picture>
<source srcset={`${loginBackAvif}, ${loginBack2xAvif} 2x`} type="image/avif" />
<source srcset={`${loginBackWebp}, ${loginBack2xWebp} 2x`} type="image/webp" />
<img
class="back-image"
src={loginBack}
style:display={$deviceInfo.docWidth <= 768 ? 'none' : 'block'}
srcset={`${loginBack} 1x, ${loginBack2x} 2x`}
alt=""
/>
</picture>
<div
style:position="fixed"
style:left={$deviceInfo.docWidth <= 480 ? '.75rem' : '1.75rem'}
style:top={'3rem'}
class="flex-row-center"
>
<LoginIcon /><span class="fs-title ml-2">{getMetadata(workbench.metadata.PlatformTitle)}</span>
</div>
<div class="panel-base" class:panel={$deviceInfo.docWidth > 768} class:white={!$themeStore.dark}>
<Scroller padding={'1rem 0'}>
<div class="form-content">
{#if page === 'onboard'}
<OnboardForm />
{:else if page === 'auth'}
<Auth />
{/if}
</div>
</Scroller>
</div>
<Popup />
</div>
</div>
<style lang="scss">
.back-image {
position: fixed;
top: 32px;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
object-position: left top;
}
.backd {
position: relative;
background-color: var(--theme-bg-color);
.bg-image {
display: flex;
flex-direction: row-reverse;
width: 100%;
height: 100%;
}
&.paneld {
background: rgba(45, 50, 160, 0.5);
.panel-base {
padding-top: 5rem;
padding-bottom: 1rem;
width: 100%;
}
}
}
.panel {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
width: 50%;
height: 100%;
min-width: 35rem;
max-width: 41rem;
background: rgba(45, 50, 160, 0.5);
mix-blend-mode: normal;
box-shadow: -30px 1.52px 173.87px #121437;
backdrop-filter: blur(157.855px);
border-radius: 1rem;
&::after {
overflow: hidden;
position: absolute;
content: '';
inset: 0;
background: radial-gradient(161.92% 96.11% at 11.33% 3.89%, #313d9a 0%, #202669 100%);
border-radius: 1rem;
z-index: -1;
}
&::before {
position: absolute;
content: '';
inset: 0;
padding: 1px;
background: conic-gradient(
rgba(255, 255, 255, 0.18) 10%,
rgba(126, 120, 165, 0.5),
rgba(191, 216, 253, 0.5),
rgba(246, 247, 249, 0.32),
rgba(219, 229, 242, 0.34) 60%,
rgba(163, 203, 255, 0.24) 90%
)
border-box;
-webkit-mask:
linear-gradient(#000 0 0) content-box,
linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
border-radius: 1rem;
transform: rotate(180deg);
transition: opacity 0.15s var(--timing-main);
opacity: 0.7;
}
}
.backd.paneld::after,
.panel::after {
overflow: hidden;
position: absolute;
content: '';
inset: 0;
background: radial-gradient(161.92% 96.11% at 11.33% 3.89%, #313d9a 0%, #202669 100%);
z-index: -1;
}
.panel::after {
border-radius: 1rem;
}
.form-content {
display: flex;
flex-direction: column;
justify-content: center;
flex-grow: 1;
height: max-content;
}
</style>
@@ -0,0 +1,92 @@
<!--
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { LoginInfo, WorkspaceLoginInfo } from '@hcengineering/login'
import { getAccount, getWorkspaces, navigateToWorkspace } from '@hcengineering/login-resources'
import { OK } from '@hcengineering/platform'
import { onMount } from 'svelte'
import { OnboardSteps } from '../index'
import onboard from '../plugin'
import { ensureConfirmed, goToLogin } from '../utils'
import Form from './Form.svelte'
import OnboardUserForm from './OnboardUserForm.svelte'
import OnboardWorkspaceForm from './OnboardWorkspaceForm.svelte'
const steps = Object.values(OnboardSteps)
let account: LoginInfo | undefined = undefined
let step: OnboardSteps = OnboardSteps.Workspace
function handleStepEvent (event: CustomEvent<LoginInfo | WorkspaceLoginInfo | undefined>): void {
if (event.detail != null) {
account = event.detail
}
handleNext(step)
}
function handleNext (current: OnboardSteps): void {
const next = steps.indexOf(current) + 1
if (next < steps.length) {
step = steps[next]
}
}
function isWorkspaceLoginInfo (info: WorkspaceLoginInfo | LoginInfo): info is WorkspaceLoginInfo {
return (info as WorkspaceLoginInfo).workspace !== undefined
}
onMount(async () => {
account = await getAccount()
if (account != null) {
await ensureConfirmed(account)
} else {
goToLogin('login')
}
const workspaces = await getWorkspaces()
if (workspaces.length > 0) {
goToLogin('selectWorkspace')
}
})
const action = {
i18n: onboard.string.StartUsingHuly,
func: async () => {
if (account !== undefined && isWorkspaceLoginInfo(account)) {
navigateToWorkspace(account.workspace, account)
}
}
}
</script>
{#if account}
{#if step === OnboardSteps.Workspace}
<OnboardWorkspaceForm {account} on:step={handleStepEvent} />
{:else if step === OnboardSteps.User}
<OnboardUserForm {account} on:step={handleStepEvent} />
{:else if step === OnboardSteps.Finish}
<Form
status={OK}
caption={onboard.string.SignUpCompleted}
subtitle={account.email}
fields={[]}
object={{}}
{action}
/>
{/if}
{/if}
@@ -0,0 +1,67 @@
<!--
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { LoginInfo } from '@hcengineering/login'
import { changeUsername } from '@hcengineering/login-resources'
import { OK, Severity, Status, unknownError } from '@hcengineering/platform'
import { createEventDispatcher } from 'svelte'
import onboard from '../plugin'
import Form from './Form.svelte'
export let account: LoginInfo
const dispatch = createEventDispatcher()
const fields = [
{ id: 'given-name', name: 'first', i18n: onboard.string.FirstName, short: true },
{ id: 'family-name', name: 'last', i18n: onboard.string.LastName, short: true }
]
const object = { first: '', last: '' }
let status: Status<any> = OK
const action = {
i18n: onboard.string.Next,
func: async () => {
status = new Status(Severity.INFO, onboard.status.ConnectingToServer, {})
try {
await changeUsername(object.first, object.last)
status = OK
dispatch('step')
} catch (err) {
status = unknownError(err)
}
}
}
</script>
<Form
caption={onboard.string.FillInProfile}
subtitle={account.email}
{status}
{fields}
{object}
{action}
bottomActions={[
{
i18n: onboard.string.Skip,
func: () => {
dispatch('step')
}
}
]}
/>
@@ -0,0 +1,52 @@
<!--
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { LoginInfo } from '@hcengineering/login'
import { createWorkspace, setLoginInfo } from '@hcengineering/login-resources'
import { Status, Severity, OK } from '@hcengineering/platform'
import { createEventDispatcher } from 'svelte'
import Form from './Form.svelte'
import onboard from '../plugin'
export let account: LoginInfo
const dispatch = createEventDispatcher()
const fields = [{ id: 'workspace', name: 'workspace', i18n: onboard.string.Workspace }]
const object = {
workspace: ''
}
let status: Status<any> = OK
const action = {
i18n: onboard.string.CreateWorkspace,
func: async () => {
status = new Status(Severity.INFO, onboard.status.ConnectingToServer, {})
const [loginStatus, result] = await createWorkspace(object.workspace)
status = loginStatus
if (result !== undefined) {
setLoginInfo(result)
dispatch('step', result)
}
}
}
</script>
<Form caption={onboard.string.CreateWorkspace} subtitle={account.email} {status} {fields} {object} {action} />
@@ -0,0 +1,44 @@
<!--
// 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 { Status } from '@hcengineering/platform'
import { Severity } from '@hcengineering/platform'
import { Status as StatusControl } from '@hcengineering/ui'
export let status: Status
</script>
{#if status.severity !== Severity.OK}
<div class="flex-row-center container" class:error={status.severity === Severity.ERROR}>
<StatusControl {status} overflow={false} />
</div>
{/if}
<style lang="scss">
.container {
padding: 0.75rem 1rem;
background-color: var(--theme-button-default);
border: 1px solid var(--theme-button-border);
border-radius: 0.5rem;
}
.error {
color: var(--system-error-color);
fill: var(--system-error-color);
background-color: var(--theme-button-default);
border-color: var(--system-error-60-color);
}
</style>
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 512 512">
<path
d="M477.867 341.333c18.851 0 34.133 15.282 34.133 34.134v102.4C512 496.718 496.718 512 477.867 512h-102.4c-18.852 0-34.134-15.282-34.134-34.133v-102.4c0-18.852 15.282-34.134 34.134-34.134zM136.533 0c18.852 0 34.134 15.282 34.134 34.133v102.4c0 18.852-15.282 34.134-34.134 34.134h-102.4C15.282 170.667 0 155.385 0 136.533v-102.4C0 15.282 15.282 0 34.133 0zm365.47 180.664A34.13 34.13 0 0 0 512 156.528V34.133C512 15.282 496.718 0 477.867 0h-102.4c-18.852 0-34.134 15.282-34.134 34.133v102.4c0 18.852-15.282 34.134-34.133 34.134H184.805a34.13 34.13 0 0 0-24.136 9.997L9.997 331.336A34.13 34.13 0 0 0 0 355.472v122.395C0 496.718 15.282 512 34.133 512h102.4c18.852 0 34.134-15.282 34.134-34.133v-102.4c0-18.852 15.282-34.134 34.133-34.134h122.395a34.13 34.13 0 0 0 24.136-9.997z"
fill="white"
/>
</svg>

After

Width:  |  Height:  |  Size: 903 B

+42
View File
@@ -0,0 +1,42 @@
//
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import type { IntlString, Resources } from '@hcengineering/platform'
import OnboardApp from './components/OnboardApp.svelte'
export default async (): Promise<Resources> => ({
component: {
OnboardApp
}
})
export const pages = ['auth', 'onboard'] as const
export enum OnboardSteps {
Workspace = 'workspace',
User = 'user',
Finish = 'finish'
}
export type Pages = (typeof pages)[number]
export interface BottomAction {
i18n: IntlString
page?: Pages
func: () => void
caption?: IntlString
}
export * from './utils'
+38
View File
@@ -0,0 +1,38 @@
/*!
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
*/
import onboard, { onboardId } from '@hcengineering/onboard'
import type { IntlString, StatusCode } from '@hcengineering/platform'
import { mergeIds } from '@hcengineering/platform'
export default mergeIds(onboardId, onboard, {
status: {
RequiredField: '' as StatusCode<{ field: string }>,
FieldsDoNotMatch: '' as StatusCode<{ field: string, field2: string }>,
ConnectingToServer: '' as StatusCode
},
string: {
CreateWorkspace: '' as IntlString,
LastName: '' as IntlString,
FirstName: '' as IntlString,
Workspace: '' as IntlString,
FillInProfile: '' as IntlString,
SetUpPassword: '' as IntlString,
Next: '' as IntlString,
Skip: '' as IntlString,
SignUpCompleted: '' as IntlString,
StartUsingHuly: '' as IntlString
}
})
+84
View File
@@ -0,0 +1,84 @@
//
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { type LoginInfo } from '@hcengineering/login'
import {
getWorkspaces,
navigateToWorkspace,
selectWorkspace,
type Pages as LoginPages
} from '@hcengineering/login-resources'
import { getMetadata } from '@hcengineering/platform'
import presentation from '@hcengineering/presentation'
import { type Location, getCurrentLocation, locationToUrl, navigate } from '@hcengineering/ui'
import { type Pages } from './index'
export function getLoc (path: Pages): Location {
const loc = getCurrentLocation()
loc.path[1] = path
loc.path.length = 2
return loc
}
export function goTo (path: Pages, clearQuery: boolean = false): void {
const loc = getLoc(path)
if (clearQuery) {
loc.query = undefined
}
navigate(loc)
}
export function goToLogin (page: LoginPages, clearQuery: boolean = false): void {
const loc = getCurrentLocation()
loc.path[0] = 'login'
loc.path[1] = page
loc.path.length = 2
if (clearQuery) {
loc.query = undefined
}
navigate(loc)
}
export function getHref (path: Pages): string {
const url = locationToUrl(getLoc(path))
const frontUrl = getMetadata(presentation.metadata.FrontUrl)
const host = frontUrl ?? document.location.origin
return host + url
}
export async function ensureConfirmed (account: LoginInfo): Promise<void> {
if (!account.confirmed) {
const loc = getCurrentLocation()
loc.path[0] = 'login'
loc.path[1] = 'confirmationSend'
loc.path.length = 2
navigate(loc)
}
}
export async function afterConfirm (): Promise<void> {
const joinedWS = await getWorkspaces()
if (joinedWS.length === 0) {
goTo('onboard')
} else if (joinedWS.length === 1) {
const result = (await selectWorkspace(joinedWS[0].workspace, null))[1]
if (result !== undefined) {
navigateToWorkspace(joinedWS[0].workspace, result)
}
} else {
goToLogin('selectWorkspace')
}
}
@@ -0,0 +1,5 @@
const sveltePreprocess = require('svelte-preprocess')
module.exports = {
preprocess: sveltePreprocess()
};
+9
View File
@@ -0,0 +1,9 @@
{
"extends": "./node_modules/@hcengineering/platform-rig/profiles/ui/tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"declarationDir": "./types"
}
}
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
}
}
+4
View File
@@ -0,0 +1,4 @@
*
!/lib/**
!CHANGELOG.md
/lib/**/__tests__/
+4
View File
@@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig"
}
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
roots: ["./src"],
coverageReporters: ["text-summary", "html"]
}
+49
View File
@@ -0,0 +1,49 @@
{
"name": "@hcengineering/onboard",
"version": "0.6.0",
"main": "lib/index.js",
"svelte": "src/index.ts",
"types": "types/index.d.ts",
"files": [
"lib/**/*",
"types/**/*",
"tsconfig.json"
],
"author": "Hardcore Engineering Inc.",
"license": "EPL-2.0",
"scripts": {
"build": "compile",
"build:watch": "compile",
"format": "format src",
"test": "jest --passWithNoTests --silent",
"_phase:build": "compile transpile src",
"_phase:test": "jest --passWithNoTests --silent",
"_phase:format": "format src",
"_phase:validate": "compile validate"
},
"devDependencies": {
"@hcengineering/platform-rig": "^0.6.0",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-n": "^15.4.0",
"eslint": "^8.54.0",
"@typescript-eslint/parser": "^6.11.0",
"eslint-config-standard-with-typescript": "^40.0.0",
"prettier": "^3.1.0",
"typescript": "^5.3.3",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.5"
},
"dependencies": {
"@hcengineering/core": "^0.6.32",
"@hcengineering/platform": "^0.6.11",
"@hcengineering/ui": "^0.6.15",
"@hcengineering/login": "^0.6.12"
},
"repository": "https://github.com/hcenginneing/anticrm",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
}
}
+27
View File
@@ -0,0 +1,27 @@
//
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import type { Plugin } from '@hcengineering/platform'
import { plugin } from '@hcengineering/platform'
import type { AnyComponent } from '@hcengineering/ui'
/** @public */
export const onboardId = 'onboard' as Plugin
export default plugin(onboardId, {
component: {
OnboardApp: '' as AnyComponent
}
})
+10
View File
@@ -0,0 +1,10 @@
{
"extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"declarationDir": "./types",
"tsBuildInfoFile": ".build/build.tsbuildinfo"
}
}
+1 -1
View File
@@ -55,7 +55,7 @@ export function registerToken (
measureCtx.info('Success auth, redirect', { email: user.email, type: 'token' })
// Successful authentication, redirect to your application
ctx.redirect(concatLink(branding?.front ?? frontUrl, '/login/auth'))
ctx.redirect(concatLink(branding?.front ?? frontUrl, '/onboard/auth'))
}
await next()
}
+15
View File
@@ -576,6 +576,21 @@
"projectFolder": "plugins/login-resources",
"shouldPublish": false
},
{
"packageName": "@hcengineering/onboard",
"projectFolder": "plugins/onboard",
"shouldPublish": true
},
{
"packageName": "@hcengineering/onboard-assets",
"projectFolder": "plugins/onboard-assets",
"shouldPublish": false
},
{
"packageName": "@hcengineering/onboard-resources",
"projectFolder": "plugins/onboard-resources",
"shouldPublish": false
},
{
"packageName": "@hcengineering/workbench",
"projectFolder": "plugins/workbench",
+2
View File
@@ -104,6 +104,7 @@
"@hcengineering/openai": "^0.6.0",
"@hcengineering/translate": "^0.6.0",
"@hcengineering/login-assets": "^0.6.0",
"@hcengineering/onboard-assets": "^0.6.0",
"@hcengineering/view-assets": "^0.6.11",
"@hcengineering/task-assets": "^0.6.19",
"@hcengineering/chunter-assets": "^0.6.18",
@@ -134,6 +135,7 @@
"@hcengineering/products-assets": "^0.1.0",
"@hcengineering/training-assets": "^0.1.0",
"@hcengineering/login": "^0.6.12",
"@hcengineering/onboard": "^0.6.0",
"@hcengineering/view": "^0.6.13",
"@hcengineering/task": "^0.6.20",
"@hcengineering/chunter": "^0.6.20",
@@ -18,6 +18,7 @@ import { leadId } from '@hcengineering/lead'
import { loginId } from '@hcengineering/login'
import { loveId } from '@hcengineering/love'
import { notificationId } from '@hcengineering/notification'
import { onboardId } from '@hcengineering/onboard'
import { preferenceId } from '@hcengineering/preference'
import { productsId } from '@hcengineering/products'
import { recruitId } from '@hcengineering/recruit'
@@ -52,6 +53,7 @@ import inventoryEn from '@hcengineering/inventory-assets/lang/en.json'
import leadEn from '@hcengineering/lead-assets/lang/en.json'
import loveEn from '@hcengineering/love-assets/lang/en.json'
import notificationEn from '@hcengineering/notification-assets/lang/en.json'
import onboardEn from '@hcengineering/onboard-assets/lang/en.json'
import preferenceEn from '@hcengineering/preference-assets/lang/en.json'
import productsEn from '@hcengineering/products-assets/lang/en.json'
import recruitEn from '@hcengineering/recruit-assets/lang/en.json'
@@ -70,6 +72,7 @@ import workbenchEn from '@hcengineering/workbench-assets/lang/en.json'
export function registerStringLoaders (): void {
addStringsLoader(coreId, async (lang: string) => coreEng)
addStringsLoader(loginId, async (lang: string) => loginEng)
addStringsLoader(onboardId, async (lang: string) => onboardEn)
addStringsLoader(platformId, async (lang: string) => platformEng)
addStringsLoader(taskId, async (lang: string) => taskEn)