From 8963009f4249967cc71a3f9262589e1d95705548 Mon Sep 17 00:00:00 2001 From: sutralia Date: Mon, 1 Jul 2024 00:41:52 +0700 Subject: [PATCH 01/12] chore: added command build release server web --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index a0aac5b3e..2b82eefa7 100644 --- a/package.json +++ b/package.json @@ -92,9 +92,9 @@ "package:server-web:mac": "yarn build:server-web && yarn pack:server-web:mac", "package:server-web:win": "yarn build:server-web && yarn pack:server-web:win", "package:server-web:linux": "yarn build:server-web && yarn pack:server-web:linux", - "build:web-server:linux:release:gh": "", - "build:web-server:mac:release": "", - "build:web-server:windows:release:gh": "" + "build:web-server:linux:release:gh": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=12288 yarn build:server-web && yarn pack:server-web:linux", + "build:web-server:mac:release": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=12288 yarn build:server-web && yarn pack:server-web:mac", + "build:web-server:windows:release:gh": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=12288 yarn build:server-web && yarn pack:server-web:win" }, "config": { "commitizen": { From 4c727df13dd380356d4f2ad14f01298afa3167b8 Mon Sep 17 00:00:00 2001 From: Ruslan Konviser Date: Sun, 30 Jun 2024 19:53:32 +0200 Subject: [PATCH 02/12] Update package.json --- package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2b82eefa7..6f6c449ae 100644 --- a/package.json +++ b/package.json @@ -92,9 +92,11 @@ "package:server-web:mac": "yarn build:server-web && yarn pack:server-web:mac", "package:server-web:win": "yarn build:server-web && yarn pack:server-web:win", "package:server-web:linux": "yarn build:server-web && yarn pack:server-web:linux", - "build:web-server:linux:release:gh": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=12288 yarn build:server-web && yarn pack:server-web:linux", + "build:web-server:linux:release": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=12288 yarn build:server-web && yarn pack:server-web:linux", + "build:web-server:linux:release:gh": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=30000 yarn build:server-web && yarn pack:server-web:linux", "build:web-server:mac:release": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=12288 yarn build:server-web && yarn pack:server-web:mac", - "build:web-server:windows:release:gh": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=12288 yarn build:server-web && yarn pack:server-web:win" + "build:web-server:windows:release": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=12288 yarn build:server-web && yarn pack:server-web:win", + "build:web-server:windows:release:gh": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=30000 yarn build:server-web && yarn pack:server-web:win" }, "config": { "commitizen": { From cc5216aa4a90721807c5911f9ebcd3f4a93624ea Mon Sep 17 00:00:00 2001 From: Ruslan Konviser Date: Sun, 30 Jun 2024 20:04:27 +0200 Subject: [PATCH 03/12] Update README.md --- apps/server-web/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/server-web/README.md b/apps/server-web/README.md index 782109f1f..844d0c40f 100644 --- a/apps/server-web/README.md +++ b/apps/server-web/README.md @@ -1,3 +1,7 @@ # Ever Teams Web Server -Electron-based Desktop App that serve Ever Teams NextJs frontend. \ No newline at end of file +Electron-based Desktop App that serves Ever Teams NextJs frontend, build with [ERB](https://github.com/electron-react-boilerplate/electron-react-boilerplate). + +## Docs + +Docs for ERB are available at . From de319d81fb9d14b1c52d24b6a7340768f3ad5eb0 Mon Sep 17 00:00:00 2001 From: Ruslan Konviser Date: Sun, 30 Jun 2024 20:43:30 +0200 Subject: [PATCH 04/12] chore: fix server apps builds --- .github/workflows/desktop-server-api.apps.yml | 6 +++--- .github/workflows/desktop-server-web.apps.yml | 6 +++--- .github/workflows/desktop.apps.yml | 6 +++--- .scripts/bump-version-electron.js | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/desktop-server-api.apps.yml b/.github/workflows/desktop-server-api.apps.yml index 110366428..58bfab1f8 100644 --- a/.github/workflows/desktop-server-api.apps.yml +++ b/.github/workflows/desktop-server-api.apps.yml @@ -58,7 +58,7 @@ jobs: with: script: | const script = require('./.scripts/bump-version-electron.js') - console.log(script.serverapi(true)) + script.serverapi(true).then(console.log) env: PROJECT_REPO: 'https://github.com/ever-co/ever-teams.git' DESKTOP_API_SERVER_APP_NAME: 'ever-teams-api-server' @@ -136,7 +136,7 @@ jobs: with: script: | const script = require('./.scripts/bump-version-electron.js') - console.log(script.serverapi(true)) + script.serverapi(true).then(console.log) env: PROJECT_REPO: 'https://github.com/ever-co/ever-teams.git' DESKTOP_API_SERVER_APP_NAME: 'ever-teams-api-server' @@ -214,7 +214,7 @@ jobs: with: script: | const script = require('./.scripts/bump-version-electron.js') - console.log(script.serverapi(true)) + script.serverapi(true).then(console.log) env: PROJECT_REPO: 'https://github.com/ever-co/ever-teams.git' DESKTOP_API_SERVER_APP_NAME: 'ever-teams-api-server' diff --git a/.github/workflows/desktop-server-web.apps.yml b/.github/workflows/desktop-server-web.apps.yml index bc27569ec..2f921a7bc 100644 --- a/.github/workflows/desktop-server-web.apps.yml +++ b/.github/workflows/desktop-server-web.apps.yml @@ -55,7 +55,7 @@ jobs: with: script: | const script = require('./.scripts/bump-version-electron.js') - console.log(script.serverweb(true)) + script.serverweb(true).then(console.log) env: PROJECT_REPO: 'https://github.com/ever-co/ever-teams.git' DESKTOP_WEB_SERVER_APP_NAME: 'ever-teams-web-server' @@ -130,7 +130,7 @@ jobs: with: script: | const script = require('./.scripts/bump-version-electron.js') - console.log(script.serverweb(true)) + script.serverweb(true).then(console.log) env: PROJECT_REPO: 'https://github.com/ever-co/ever-teams.git' DESKTOP_WEB_SERVER_APP_NAME: 'ever-teams-web-server' @@ -205,7 +205,7 @@ jobs: with: script: | const script = require('./.scripts/bump-version-electron.js') - console.log(script.serverweb(true)) + script.serverweb(true).then(console.log) env: PROJECT_REPO: 'https://github.com/ever-co/ever-teams.git' DESKTOP_WEB_SERVER_APP_NAME: 'ever-teams-web-server' diff --git a/.github/workflows/desktop.apps.yml b/.github/workflows/desktop.apps.yml index 47f63b1b8..8a192a06e 100644 --- a/.github/workflows/desktop.apps.yml +++ b/.github/workflows/desktop.apps.yml @@ -58,7 +58,7 @@ jobs: with: script: | const script = require('./.scripts/bump-version-electron.js') - console.log(script.desktopTimer(true)) + script.desktopTimer(true).then(console.log) env: PROJECT_REPO: 'https://github.com/ever-co/ever-teams.git' DESKTOP_TIMER_APP_NAME: 'ever-teams-desktop' @@ -136,7 +136,7 @@ jobs: with: script: | const script = require('./.scripts/bump-version-electron.js') - console.log(script.desktopTimer(true)) + script.desktopTimer(true).then(console.log) env: PROJECT_REPO: 'https://github.com/ever-co/ever-teams.git' DESKTOP_TIMER_APP_NAME: 'ever-teams-desktop' @@ -214,7 +214,7 @@ jobs: with: script: | const script = require('./.scripts/bump-version-electron.js') - console.log(script.desktopTimer(true)) + script.desktopTimer(true).then(console.log) env: PROJECT_REPO: 'https://github.com/ever-co/ever-teams.git' DESKTOP_TIMER_APP_NAME: 'ever-teams-desktop' diff --git a/.scripts/bump-version-electron.js b/.scripts/bump-version-electron.js index a81d7500a..a2680dd12 100644 --- a/.scripts/bump-version-electron.js +++ b/.scripts/bump-version-electron.js @@ -31,8 +31,8 @@ async function getLatestTag(repoURL) { } module.exports.serverweb = async (isProd) => { - if (fs.existsSync('./apps/server-web/src/package.json')) { - let package = require('../apps/server-web/src/package.json'); + if (fs.existsSync('./apps/server-web/release/app/package.json')) { + let package = require('../apps/server-web/release/app/package.json'); let currentVersion = package.version; const repoURL = process.env.PROJECT_REPO; @@ -104,9 +104,9 @@ module.exports.serverweb = async (isProd) => { ]; } - fs.writeFileSync('./apps/server-web/src/package.json', JSON.stringify(package, null, 2)); + fs.writeFileSync('./apps/server-web/release/app/package.json', JSON.stringify(package, null, 2)); - let updated = require('../apps/server-web/src/package.json'); + let updated = require('../apps/server-web/release/app/package.json'); console.log('Version releasing', updated.version); } From e93a48799240f099233173a6b815248c57ee16b3 Mon Sep 17 00:00:00 2001 From: Ruslan Konviser Date: Sun, 30 Jun 2024 22:11:49 +0200 Subject: [PATCH 05/12] chore: web server build fixes --- .scripts/bump-version-electron.js | 41 ++++++++++++++++++- apps/server-web/package.json | 4 +- apps/server-web/release/app/package-lock.json | 4 +- apps/server-web/release/app/package.json | 2 +- 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/.scripts/bump-version-electron.js b/.scripts/bump-version-electron.js index a2680dd12..c24f65830 100644 --- a/.scripts/bump-version-electron.js +++ b/.scripts/bump-version-electron.js @@ -61,6 +61,43 @@ module.exports.serverweb = async (isProd) => { package.description = process.env.DESKTOP_WEB_SERVER_APP_DESCRIPTION; package.homepage = process.env.COMPANY_SITE_LINK; + fs.writeFileSync('./apps/server-web/release/app/package.json', JSON.stringify(package, null, 2)); + + let updated = require('../apps/server-web/release/app/package.json'); + + console.log('Version releasing', updated.version); + } + + if (fs.existsSync('./apps/server-web/package.json')) { + let package = require('../apps/server-web/package.json'); + let currentVersion = package.version; + + const repoURL = process.env.PROJECT_REPO; + console.log('repoURL', repoURL); + + const appName = process.env.DESKTOP_WEB_SERVER_APP_NAME; + console.log('appName', appName); + + const stdout = await getLatestTag(repoURL); + + let newVersion = stdout.trim(); + console.log('latest tag', newVersion); + + if (newVersion) { + // let's remove "v" from version, i.e. first character + newVersion = newVersion.substring(1); + package.version = newVersion; + + console.log('Version updated to version', newVersion); + } else { + console.log('Latest tag is not found. Build Desktop Web Server App with default version', currentVersion); + } + + package.name = appName; + package.productName = process.env.DESKTOP_WEB_SERVER_APP_DESCRIPTION; + package.description = process.env.DESKTOP_WEB_SERVER_APP_DESCRIPTION; + package.homepage = process.env.COMPANY_SITE_LINK; + package.build.appId = process.env.DESKTOP_WEB_SERVER_APP_ID; package.build.productName = process.env.DESKTOP_WEB_SERVER_APP_DESCRIPTION; package.build.linux.executableName = appName; @@ -104,9 +141,9 @@ module.exports.serverweb = async (isProd) => { ]; } - fs.writeFileSync('./apps/server-web/release/app/package.json', JSON.stringify(package, null, 2)); + fs.writeFileSync('./apps/server-web/package.json', JSON.stringify(package, null, 2)); - let updated = require('../apps/server-web/release/app/package.json'); + let updated = require('../apps/server-web/package.json'); console.log('Version releasing', updated.version); } diff --git a/apps/server-web/package.json b/apps/server-web/package.json index 0bd279f15..cf5782d0e 100644 --- a/apps/server-web/package.json +++ b/apps/server-web/package.json @@ -1,5 +1,5 @@ { - "name": "ever-teams-server-web", + "name": "ever-teams-web-server", "version": "0.1.0", "description": "Ever Teams Web Server", "license": "AGPL-3.0", @@ -164,7 +164,7 @@ "yarn": ">=1.13.0" }, "build": { - "appId": "co.ever.teamswebserver", + "appId": "com.ever.everteamswebserver", "artifactName": "${name}-${version}.${ext}", "productName": "Ever Teams Web Server", "copyright": "Copyright © 2024-Present. Ever Co. LTD", diff --git a/apps/server-web/release/app/package-lock.json b/apps/server-web/release/app/package-lock.json index 07dfc2c51..dd9ee8b5b 100644 --- a/apps/server-web/release/app/package-lock.json +++ b/apps/server-web/release/app/package-lock.json @@ -1,11 +1,11 @@ { - "name": "ever-teams-server-web", + "name": "ever-teams-web-server", "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "ever-teams-server-web", + "name": "ever-teams-web-server", "version": "0.1.0", "hasInstallScript": true, "license": "AGPL-3.0" diff --git a/apps/server-web/release/app/package.json b/apps/server-web/release/app/package.json index 2d7d161d7..1c6292912 100644 --- a/apps/server-web/release/app/package.json +++ b/apps/server-web/release/app/package.json @@ -1,5 +1,5 @@ { - "name": "ever-teams-server-web", + "name": "ever-teams-web-server", "version": "0.1.0", "description": "Ever Teams Web Server", "license": "AGPL-3.0", From 7c073133990065a9bedb6e8e05390b70ca7be54c Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Thu, 4 Jul 2024 16:25:33 +0200 Subject: [PATCH 06/12] feat: time tracking saving tracked time and update total worked --- apps/web/app/hooks/features/useTimer.ts | 61 ++++++++++++++----------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/apps/web/app/hooks/features/useTimer.ts b/apps/web/app/hooks/features/useTimer.ts index c87d48021..a2d1c9377 100644 --- a/apps/web/app/hooks/features/useTimer.ts +++ b/apps/web/app/hooks/features/useTimer.ts @@ -84,27 +84,27 @@ function useLocalTimeCounter(timerStatus: ITimerStatus | null, activeTeamTask: I // Update local time status (storage and store) only when global timerStatus changes useEffect(() => { - if (firstLoad) { - const localStatus = getLocalCounterStatus(); - localStatus && setLocalTimerStatus(localStatus); - - const timerStatusDate = timerStatus?.lastLog?.createdAt - ? moment(timerStatus?.lastLog?.createdAt).unix() * 1000 - timerStatus?.lastLog?.duration - : 0; - - timerStatus && - updateLocalTimerStatus({ - runnedDateTime: - (timerStatus.running ? timerStatusDate || Date.now() : 0) || localStatus?.runnedDateTime || 0, - running: timerStatus.running, - lastTaskId: timerStatus.lastLog?.taskId || null - }); - } + // if (firstLoad) { + const localStatus = getLocalCounterStatus(); + localStatus && setLocalTimerStatus(localStatus); + + const timerStatusDate = timerStatus?.lastLog?.createdAt + ? moment(timerStatus?.lastLog?.createdAt).unix() * 1000 - timerStatus?.lastLog?.duration + : 0; + + timerStatus && + updateLocalTimerStatus({ + runnedDateTime: + (timerStatus.running ? timerStatusDate || Date.now() : 0) || localStatus?.runnedDateTime || 0, + running: timerStatus.running, + lastTaskId: timerStatus.lastLog?.taskId || null + }); + // } }, [firstLoad, timerStatus, getLocalCounterStatus, setLocalTimerStatus, updateLocalTimerStatus]); // THis is form constant update of the progress line timerSecondsRef.current = useMemo(() => { - if (!firstLoad) return 0; + // if (!firstLoad) return 0; if (seconds > timerSecondsRef.current) { return seconds; } @@ -115,16 +115,16 @@ function useLocalTimeCounter(timerStatus: ITimerStatus | null, activeTeamTask: I }, [seconds, firstLoad, timerStatusRef]); useEffect(() => { - if (firstLoad) { - timerSecondsRef.current = 0; - setTimerSeconds(0); - } + // if (firstLoad) { + timerSecondsRef.current = 0; + setTimerSeconds(0); + // } }, [activeTeamTask?.id, setTimerSeconds, firstLoad, timerSecondsRef]); useEffect(() => { - if (firstLoad) { - setTimerSeconds(timerSecondsRef.current); - } + // if (firstLoad) { + setTimerSeconds(timerSecondsRef.current); + // } }, [setTimerSeconds, firstLoad]); // Time Counter @@ -271,9 +271,9 @@ export function useTimer() { // Loading states useEffect(() => { - if (firstLoad) { - setTimerStatusFetching(loading); - } + // if (firstLoad) { + setTimerStatusFetching(loading); + // } }, [loading, firstLoad, setTimerStatusFetching]); useEffect(() => { @@ -355,11 +355,18 @@ export function useTimer() { running: false }); + syncTimer(); + return stopTimerQueryCall(timerStatus?.lastLog?.source || TimerSource.TEAMS).then((res) => { res.data && !isEqual(timerStatus, res.data) && setTimerStatus(res.data); }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [timerStatus, setTimerStatus, stopTimerQueryCall, taskId, updateLocalTimerStatus]); + useEffect(() => { + console.log(timerStatus); + }, [timerStatus]); + // If active team changes then stop the timer useEffect(() => { if ( From d3e252aa5533f6281f90d504a9888aeec5979003 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Thu, 4 Jul 2024 17:54:35 +0200 Subject: [PATCH 07/12] feat: update time progess and working time while tracking --- apps/web/app/hooks/features/useTimer.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/web/app/hooks/features/useTimer.ts b/apps/web/app/hooks/features/useTimer.ts index a2d1c9377..fd64d7950 100644 --- a/apps/web/app/hooks/features/useTimer.ts +++ b/apps/web/app/hooks/features/useTimer.ts @@ -364,8 +364,13 @@ export function useTimer() { }, [timerStatus, setTimerStatus, stopTimerQueryCall, taskId, updateLocalTimerStatus]); useEffect(() => { - console.log(timerStatus); - }, [timerStatus]); + if (timerStatus?.running) { + const syncTimerInterval = setInterval(() => { + syncTimer(); + }, 60000); + return () => clearInterval(syncTimerInterval); + } + }, [syncTimer, timerStatus]); // If active team changes then stop the timer useEffect(() => { From bdbfe0a3eff11dbcb36066d5f81f28e66bad50f3 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Thu, 4 Jul 2024 19:02:36 +0200 Subject: [PATCH 08/12] fix: clearInterval from useEffect task tracking --- apps/web/app/hooks/features/useTimer.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/web/app/hooks/features/useTimer.ts b/apps/web/app/hooks/features/useTimer.ts index fd64d7950..432a452c1 100644 --- a/apps/web/app/hooks/features/useTimer.ts +++ b/apps/web/app/hooks/features/useTimer.ts @@ -364,12 +364,15 @@ export function useTimer() { }, [timerStatus, setTimerStatus, stopTimerQueryCall, taskId, updateLocalTimerStatus]); useEffect(() => { + let syncTimerInterval: NodeJS.Timeout; if (timerStatus?.running) { - const syncTimerInterval = setInterval(() => { + syncTimerInterval = setInterval(() => { syncTimer(); }, 60000); - return () => clearInterval(syncTimerInterval); } + return () => { + if (syncTimerInterval) clearInterval(syncTimerInterval); + }; }, [syncTimer, timerStatus]); // If active team changes then stop the timer From 4f6c3c29bde0378ccd6545a756a3b1cd258437c4 Mon Sep 17 00:00:00 2001 From: "Gloire Mutaliko (Salva)" <86450367+GloireMutaliko21@users.noreply.github.com> Date: Fri, 5 Jul 2024 18:44:19 +0200 Subject: [PATCH 09/12] Fix/login with one team workspace (#2685) * feat: skip select team/workspace for member with only one (magic link) * feat: skip team and workspace selection for user with only one * feat: auto redirect user with one team signin with password * fix: broken style for workspace screen --- .../app/[locale]/auth/passcode/component.tsx | 55 ++++++++++++++----- .../app/[locale]/auth/password/component.tsx | 45 ++++++++++----- apps/web/locales/ar.json | 1 + apps/web/locales/bg.json | 1 + apps/web/locales/de.json | 1 + apps/web/locales/en.json | 1 + apps/web/locales/es.json | 1 + apps/web/locales/fr.json | 1 + apps/web/locales/he.json | 1 + apps/web/locales/it.json | 1 + apps/web/locales/nl.json | 1 + apps/web/locales/pl.json | 1 + apps/web/locales/pt.json | 1 + apps/web/locales/ru.json | 1 + apps/web/locales/zh.json | 1 + 15 files changed, 83 insertions(+), 30 deletions(-) diff --git a/apps/web/app/[locale]/auth/passcode/component.tsx b/apps/web/app/[locale]/auth/passcode/component.tsx index 5cc5cb0ad..b133743f2 100644 --- a/apps/web/app/[locale]/auth/passcode/component.tsx +++ b/apps/web/app/[locale]/auth/passcode/component.tsx @@ -4,7 +4,17 @@ import { getAccessTokenCookie, getActiveUserIdCookie } from '@app/helpers'; import { TAuthenticationPasscode, useAuthenticationPasscode } from '@app/hooks'; import { IClassName, ISigninEmailConfirmWorkspaces } from '@app/interfaces'; import { clsxm } from '@app/utils'; -import { AuthCodeInputField, Avatar, BackButton, Button, Card, InputField, SpinnerLoader, Text } from 'lib/components'; +import { + AuthCodeInputField, + Avatar, + BackButton, + BackdropLoader, + Button, + Card, + InputField, + SpinnerLoader, + Text +} from 'lib/components'; import { CircleIcon, CheckCircleOutlineIcon } from 'assets/svg'; import { AuthLayout } from 'lib/layout'; import { useTranslations } from 'next-intl'; @@ -298,6 +308,7 @@ function PasscodeScreen({ form, className }: { form: TAuthenticationPasscode } & } function WorkSpaceScreen({ form, className }: { form: TAuthenticationPasscode } & IClassName) { + const t = useTranslations(); const [selectedWorkspace, setSelectedWorkspace] = useState(0); const [selectedTeam, setSelectedTeam] = useState(''); const router = useRouter(); @@ -339,21 +350,35 @@ function WorkSpaceScreen({ form, className }: { form: TAuthenticationPasscode } } }, [form.authScreen, router]); + const hasMultipleTeams = form.workspaces.some((workspace) => workspace.current_teams.length > 1); + return ( - { - form.authScreen.setScreen('email'); - form.setErrors({}); - }} - selectedWorkspace={selectedWorkspace} - setSelectedWorkspace={setSelectedWorkspace} - setSelectedTeam={setSelectedTeam} - selectedTeam={selectedTeam} - signInWorkspaceLoading={form.signInWorkspaceLoading} - /> + <> + {/* The workspace component will be visible only if there are two or many workspaces and/or teams */} +
+ { + form.authScreen.setScreen('email'); + form.setErrors({}); + }} + selectedWorkspace={selectedWorkspace} + setSelectedWorkspace={setSelectedWorkspace} + setSelectedTeam={setSelectedTeam} + selectedTeam={selectedTeam} + signInWorkspaceLoading={form.signInWorkspaceLoading} + /> +
+ + {/* If the user is a member of only one workspace and only one team, render a redirecting component */} + {form.workspaces.length === 1 && !hasMultipleTeams && ( +
+ +
+ )} + ); } diff --git a/apps/web/app/[locale]/auth/password/component.tsx b/apps/web/app/[locale]/auth/password/component.tsx index 2876dd839..08dcba15c 100644 --- a/apps/web/app/[locale]/auth/password/component.tsx +++ b/apps/web/app/[locale]/auth/password/component.tsx @@ -4,7 +4,7 @@ import { getAccessTokenCookie } from '@app/helpers'; import { TAuthenticationPassword, useAuthenticationPassword } from '@app/hooks'; import { IClassName } from '@app/interfaces'; import { clsxm } from '@app/utils'; -import { Button, Card, InputField, Text } from 'lib/components'; +import { BackdropLoader, Button, Card, InputField, Text } from 'lib/components'; import { AuthLayout } from 'lib/layout'; import { useTranslations } from 'next-intl'; import Link from 'next/link'; @@ -99,6 +99,7 @@ function LoginForm({ form }: { form: TAuthenticationPassword }) { } function WorkSpaceScreen({ form, className }: { form: TAuthenticationPassword } & IClassName) { + const t = useTranslations(); const [selectedWorkspace, setSelectedWorkspace] = useState(0); const [selectedTeam, setSelectedTeam] = useState(''); const router = useRouter(); @@ -140,20 +141,34 @@ function WorkSpaceScreen({ form, className }: { form: TAuthenticationPassword } } }, [form.authScreen, router]); + const hasMultipleTeams = form.workspaces.some((workspace) => workspace.current_teams.length > 1); + return ( - { - form.authScreen.setScreen('login'); - form.setErrors({}); - }} - selectedWorkspace={selectedWorkspace} - setSelectedWorkspace={setSelectedWorkspace} - setSelectedTeam={setSelectedTeam} - selectedTeam={selectedTeam} - signInWorkspaceLoading={form.signInWorkspaceLoading} - /> + <> + {/* The workspace component will be visible only if there are two or many workspaces and/or teams */} +
+ { + form.authScreen.setScreen('login'); + form.setErrors({}); + }} + selectedWorkspace={selectedWorkspace} + setSelectedWorkspace={setSelectedWorkspace} + setSelectedTeam={setSelectedTeam} + selectedTeam={selectedTeam} + signInWorkspaceLoading={form.signInWorkspaceLoading} + /> +
+ + {/* If the user is a member of only one workspace and only one team, render a redirecting component */} + {form.workspaces.length === 1 && !hasMultipleTeams && ( +
+ +
+ )} + ); } diff --git a/apps/web/locales/ar.json b/apps/web/locales/ar.json index 71495801e..031e4d8ba 100644 --- a/apps/web/locales/ar.json +++ b/apps/web/locales/ar.json @@ -312,6 +312,7 @@ "HEADING_TITLE": "إنشاء فريق جديد", "HEADING_DESCRIPTION": "يرجى إدخال تفاصيل فريقك لإنشاء فريق جديد.", "LOADING_TEXT": "نحن الآن ننشئ مساحة عملك الجديدة، انتظر...", + "REDIRECT_TO_WORSPACE_LOADING": "إعادة التوجيه إلى مساحة العمل الخاصة بك", "VERIFY_EMAIL_LOADING_TEXT": "نحن نحقق من بريدك الإلكتروني، انتظر...", "INPUT_TEAM_NAME": "أدخل اسم فريقك", "JOIN_EXISTING_TEAM": "الانضمام إلى فريق موجود؟", diff --git a/apps/web/locales/bg.json b/apps/web/locales/bg.json index 05e3a4394..a9eb65082 100644 --- a/apps/web/locales/bg.json +++ b/apps/web/locales/bg.json @@ -312,6 +312,7 @@ "HEADING_TITLE": "Създаване на нов отбор", "HEADING_DESCRIPTION": "Моля, въведете данните на вашия отбор, за да създадете нов отбор.", "LOADING_TEXT": "В момента създаваме новото ви работно място, изчакайте...", + "REDIRECT_TO_WORSPACE_LOADING": "Пренасочване към вашето работно пространство...", "VERIFY_EMAIL_LOADING_TEXT": "Проверяваме имейла ви, изчакайте...", "INPUT_TEAM_NAME": "Въведете име на отбора", "JOIN_EXISTING_TEAM": "Присъединявате се към съществуващ отбор?", diff --git a/apps/web/locales/de.json b/apps/web/locales/de.json index 6c412848d..3920c8d1b 100644 --- a/apps/web/locales/de.json +++ b/apps/web/locales/de.json @@ -312,6 +312,7 @@ "HEADING_TITLE": "Neues Team erstellen", "HEADING_DESCRIPTION": "Bitte geben Sie die Details für Ihr neues Team ein, um ein neues Team zu erstellen.", "LOADING_TEXT": "Wir erstellen gerade Ihren neuen Arbeitsbereich, bitte warten...", + "REDIRECT_TO_WORSPACE_LOADING": "Weiterleitung zu Ihrem Arbeitsbereich...", "VERIFY_EMAIL_LOADING_TEXT": "Wir verifizieren gerade Ihre E-Mail, bitte warten...", "INPUT_TEAM_NAME": "Teamnamen eingeben", "JOIN_EXISTING_TEAM": "Einem bestehenden Team beitreten?", diff --git a/apps/web/locales/en.json b/apps/web/locales/en.json index 23c2cb9c4..955d35e9d 100644 --- a/apps/web/locales/en.json +++ b/apps/web/locales/en.json @@ -312,6 +312,7 @@ "HEADING_TITLE": "Create New Team", "HEADING_DESCRIPTION": "Please enter your team details to create a new team.", "LOADING_TEXT": "We are now creating your new workplace, hold on...", + "REDIRECT_TO_WORSPACE_LOADING": "Redirecting to your workspace...", "VERIFY_EMAIL_LOADING_TEXT": "We are verifying your email, hold on...", "INPUT_TEAM_NAME": "Input your team name", "JOIN_EXISTING_TEAM": "Joining existing team?", diff --git a/apps/web/locales/es.json b/apps/web/locales/es.json index 48245f713..a11845ac9 100644 --- a/apps/web/locales/es.json +++ b/apps/web/locales/es.json @@ -312,6 +312,7 @@ "HEADING_TITLE": "Crear nuevo equipo", "HEADING_DESCRIPTION": "Por favor ingrese los detalles de su equipo para crear un nuevo equipo.", "LOADING_TEXT": "Ahora estamos creando su nuevo lugar de trabajo, espere...", + "REDIRECT_TO_WORSPACE_LOADING": "Redirigiendo a tu espacio de trabajo...", "VERIFY_EMAIL_LOADING_TEXT": "Estamos verificando su correo electrónico, espere...", "INPUT_TEAM_NAME": "Ingrese el nombre de su equipo", "JOIN_EXISTING_TEAM": "¿Unirse a un equipo existente?", diff --git a/apps/web/locales/fr.json b/apps/web/locales/fr.json index 58ac24882..575f3ab38 100644 --- a/apps/web/locales/fr.json +++ b/apps/web/locales/fr.json @@ -312,6 +312,7 @@ "HEADING_TITLE": "Créer une nouvelle équipe", "HEADING_DESCRIPTION": "Veuillez entrer les détails de votre équipe pour créer une nouvelle équipe.", "LOADING_TEXT": "Nous créons maintenant votre nouvel espace de travail, patientez...", + "REDIRECT_TO_WORSPACE_LOADING": "Redirection vers votre espace de travail...", "VERIFY_EMAIL_LOADING_TEXT": "Nous vérifions votre e-mail, patientez...", "INPUT_TEAM_NAME": "Saisissez le nom de votre équipe", "JOIN_EXISTING_TEAM": "Rejoindre une équipe existante ?", diff --git a/apps/web/locales/he.json b/apps/web/locales/he.json index de09d3472..482415d4e 100644 --- a/apps/web/locales/he.json +++ b/apps/web/locales/he.json @@ -312,6 +312,7 @@ "HEADING_TITLE": "צור צוות חדש", "HEADING_DESCRIPTION": "אנא הכנס את פרטי הצוות שלך כדי ליצור צוות חדש.", "LOADING_TEXT": "אנחנו עכשיו יוצרים את סביבת העבודה החדשה שלך, רגע קטן...", + "REDIRECT_TO_WORSPACE_LOADING": "מכוון מחדש לחלל העבודה שלך", "VERIFY_EMAIL_LOADING_TEXT": "אנחנו מאמתים את האימייל שלך, רגע קטן...", "INPUT_TEAM_NAME": "הכנס את שם הצוות שלך", "JOIN_EXISTING_TEAM": "מצטרף לצוות קיים?", diff --git a/apps/web/locales/it.json b/apps/web/locales/it.json index 59a30ce9f..6a74c2a4e 100644 --- a/apps/web/locales/it.json +++ b/apps/web/locales/it.json @@ -312,6 +312,7 @@ "HEADING_TITLE": "Crea un Nuovo Team", "HEADING_DESCRIPTION": "Inserisci i dettagli del tuo team per crearne uno nuovo.", "LOADING_TEXT": "Stiamo creando il tuo nuovo spazio di lavoro, attendi...", + "REDIRECT_TO_WORSPACE_LOADING": "Reindirizzamento al tuo spazio di lavoro...", "VERIFY_EMAIL_LOADING_TEXT": "Stiamo verificando la tua email, attendi...", "INPUT_TEAM_NAME": "Inserisci il nome del tuo team", "JOIN_EXISTING_TEAM": "Unisciti a un team esistente?", diff --git a/apps/web/locales/nl.json b/apps/web/locales/nl.json index 3748ca039..379580428 100644 --- a/apps/web/locales/nl.json +++ b/apps/web/locales/nl.json @@ -312,6 +312,7 @@ "HEADING_TITLE": "Nieuw team maken", "HEADING_DESCRIPTION": "Voer uw teamgegevens in om een nieuw team te maken.", "LOADING_TEXT": "We maken nu uw nieuwe werkplek aan, even geduld...", + "REDIRECT_TO_WORSPACE_LOADING": "Doorverwijzen naar uw werkruimte...", "VERIFY_EMAIL_LOADING_TEXT": "We controleren uw e-mailadres, even geduld...", "INPUT_TEAM_NAME": "Voer uw teamnaam in", "JOIN_EXISTING_TEAM": "Bestaand team joinen?", diff --git a/apps/web/locales/pl.json b/apps/web/locales/pl.json index e6baa5879..2c338afe1 100644 --- a/apps/web/locales/pl.json +++ b/apps/web/locales/pl.json @@ -312,6 +312,7 @@ "HEADING_TITLE": "Stwórz Nowy Zespół", "HEADING_DESCRIPTION": "Proszę podać szczegóły swojego zespołu, aby stworzyć nowy zespół.", "LOADING_TEXT": "Teraz tworzymy twoje nowe miejsce pracy, proszę czekać...", + "REDIRECT_TO_WORSPACE_LOADING": "Przekierowywanie do Twojej przestrzeni roboczej...", "VERIFY_EMAIL_LOADING_TEXT": "Weryfikujemy twój e-mail, proszę czekać...", "INPUT_TEAM_NAME": "Wprowadź nazwę swojego zespołu", "JOIN_EXISTING_TEAM": "Dołączasz do istniejącego zespołu?", diff --git a/apps/web/locales/pt.json b/apps/web/locales/pt.json index da21ac9f7..fbf63bcfd 100644 --- a/apps/web/locales/pt.json +++ b/apps/web/locales/pt.json @@ -312,6 +312,7 @@ "HEADING_TITLE": "Criar Nova Equipe", "HEADING_DESCRIPTION": "Por favor, insira os detalhes da sua equipe para criar uma nova equipe.", "LOADING_TEXT": "Estamos criando o seu novo ambiente de trabalho, aguarde...", + "REDIRECT_TO_WORSPACE_LOADING": "Redirecionando para o seu espaço de trabalho...", "VERIFY_EMAIL_LOADING_TEXT": "Estamos verificando o seu e-mail, aguarde...", "INPUT_TEAM_NAME": "Insira o nome da sua equipe", "JOIN_EXISTING_TEAM": "Já faz parte de uma equipe?", diff --git a/apps/web/locales/ru.json b/apps/web/locales/ru.json index 73ab9203b..060ea4d32 100644 --- a/apps/web/locales/ru.json +++ b/apps/web/locales/ru.json @@ -312,6 +312,7 @@ "HEADING_TITLE": "Создать новую команду", "HEADING_DESCRIPTION": "Введите данные вашей команды, чтобы создать новую команду.", "LOADING_TEXT": "Мы сейчас создаем ваше новое рабочее место, подождите...", + "REDIRECT_TO_WORSPACE_LOADING": "Перенаправление на ваше рабочее пространство...", "VERIFY_EMAIL_LOADING_TEXT": "Мы проверяем вашу электронную почту, подождите...", "INPUT_TEAM_NAME": "Введите название вашей команды", "JOIN_EXISTING_TEAM": "Присоединиться к существующей команде?", diff --git a/apps/web/locales/zh.json b/apps/web/locales/zh.json index 5102546f0..d9d68c603 100644 --- a/apps/web/locales/zh.json +++ b/apps/web/locales/zh.json @@ -312,6 +312,7 @@ "HEADING_TITLE": "创建新团队", "HEADING_DESCRIPTION": "请输入团队信息以创建新团队。", "LOADING_TEXT": "正在为您创建新的工作区,请稍候......", + "REDIRECT_TO_WORSPACE_LOADING": "正在重定向到您的工作空间......", "VERIFY_EMAIL_LOADING_TEXT": "正在验证您的电子邮箱,请稍候......", "INPUT_TEAM_NAME": "输入您的团队名称", "JOIN_EXISTING_TEAM": "加入已有团队?", From 88332ddf5904644cd9d6962973a31bb4175b1a2a Mon Sep 17 00:00:00 2001 From: "Gloire Mutaliko (Salva)" <86450367+GloireMutaliko21@users.noreply.github.com> Date: Fri, 5 Jul 2024 18:45:09 +0200 Subject: [PATCH 10/12] feat: store and remember the latest selected workspace and team (#2686) --- .../app/[locale]/auth/passcode/component.tsx | 12 +++++++- .../app/[locale]/auth/password/component.tsx | 12 +++++++- .../app/[locale]/auth/workspace/component.tsx | 30 ++++++++++++++++++- apps/web/app/constants.ts | 4 +++ 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/apps/web/app/[locale]/auth/passcode/component.tsx b/apps/web/app/[locale]/auth/passcode/component.tsx index b133743f2..05e51941a 100644 --- a/apps/web/app/[locale]/auth/passcode/component.tsx +++ b/apps/web/app/[locale]/auth/passcode/component.tsx @@ -26,6 +26,7 @@ import stc from 'string-to-color'; import { ScrollArea, ScrollBar } from '@components/ui/scroll-bar'; import SocialLogins from '../social-logins-buttons'; import { useSession } from 'next-auth/react'; +import { LAST_WORSPACE_AND_TEAM, USER_SAW_OUTSTANDING_NOTIFICATION } from '@app/constants'; function AuthPasscode() { const form = useAuthenticationPasscode(); @@ -317,7 +318,8 @@ function WorkSpaceScreen({ form, className }: { form: TAuthenticationPasscode } (e: any) => { if (typeof selectedWorkspace !== 'undefined') { form.handleWorkspaceSubmit(e, form.workspaces[selectedWorkspace].token, selectedTeam); - window && window?.localStorage.removeItem('user-saw-notif'); + window && window?.localStorage.removeItem(USER_SAW_OUTSTANDING_NOTIFICATION); + window && window?.localStorage.setItem(LAST_WORSPACE_AND_TEAM, selectedTeam); } }, [selectedWorkspace, selectedTeam, form] @@ -332,6 +334,14 @@ function WorkSpaceScreen({ form, className }: { form: TAuthenticationPasscode } if (form.workspaces.length === 1 && currentTeams?.length === 1) { setSelectedTeam(currentTeams[0].team_id); + } else { + const lastSelectedTeam = window.localStorage.getItem(LAST_WORSPACE_AND_TEAM) || currentTeams[0].team_id; + const lastSelectedWorkspace = + form.workspaces.findIndex((workspace) => + workspace.current_teams.find((team) => team.team_id === lastSelectedTeam) + ) || 0; + setSelectedTeam(lastSelectedTeam); + setSelectedWorkspace(lastSelectedWorkspace); } if (form.workspaces.length === 1 && (currentTeams?.length || 0) <= 1) { diff --git a/apps/web/app/[locale]/auth/password/component.tsx b/apps/web/app/[locale]/auth/password/component.tsx index 08dcba15c..b50d94bbc 100644 --- a/apps/web/app/[locale]/auth/password/component.tsx +++ b/apps/web/app/[locale]/auth/password/component.tsx @@ -12,6 +12,7 @@ import { useRouter } from 'next/navigation'; import { useCallback, useEffect, useState } from 'react'; import { WorkSpaceComponent } from '../passcode/component'; import SocialLogins from '../social-logins-buttons'; +import { LAST_WORSPACE_AND_TEAM, USER_SAW_OUTSTANDING_NOTIFICATION } from '@app/constants'; export default function AuthPassword() { const t = useTranslations(); @@ -108,7 +109,8 @@ function WorkSpaceScreen({ form, className }: { form: TAuthenticationPassword } (e: any) => { if (typeof selectedWorkspace !== 'undefined') { form.handleWorkspaceSubmit(e, form.workspaces[selectedWorkspace].token, selectedTeam); - window && window?.localStorage.removeItem('user-saw-notif'); + window && window?.localStorage.removeItem(USER_SAW_OUTSTANDING_NOTIFICATION); + window && window?.localStorage.setItem(LAST_WORSPACE_AND_TEAM, selectedTeam); } }, [selectedWorkspace, selectedTeam, form] @@ -123,6 +125,14 @@ function WorkSpaceScreen({ form, className }: { form: TAuthenticationPassword } if (form.workspaces.length === 1 && currentTeams?.length === 1) { setSelectedTeam(currentTeams[0].team_id); + } else { + const lastSelectedTeam = window.localStorage.getItem(LAST_WORSPACE_AND_TEAM) || currentTeams[0].team_id; + const lastSelectedWorkspace = + form.workspaces.findIndex((workspace) => + workspace.current_teams.find((team) => team.team_id === lastSelectedTeam) + ) || 0; + setSelectedTeam(lastSelectedTeam); + setSelectedWorkspace(lastSelectedWorkspace); } if (form.workspaces.length === 1 && (currentTeams?.length || 0) <= 1) { diff --git a/apps/web/app/[locale]/auth/workspace/component.tsx b/apps/web/app/[locale]/auth/workspace/component.tsx index 6bc8a2778..57605412c 100644 --- a/apps/web/app/[locale]/auth/workspace/component.tsx +++ b/apps/web/app/[locale]/auth/workspace/component.tsx @@ -10,6 +10,7 @@ import { useAuthenticationSocialLogin } from '@app/hooks/auth/useAuthenticationS import { ISigninEmailConfirmWorkspaces } from '@app/interfaces'; import Cookies from 'js-cookie'; import { useSession } from 'next-auth/react'; +import { LAST_WORSPACE_AND_TEAM, USER_SAW_OUTSTANDING_NOTIFICATION } from '@app/constants'; export default function SocialLoginChooseWorspace() { const t = useTranslations(); @@ -57,6 +58,32 @@ function WorkSpaceScreen() { loadOAuthSession(); }, [session]); + useEffect(() => { + if (workspaces.length === 1) { + setSelectedWorkspace(0); + } + + const currentTeams = workspaces[0]?.current_teams; + + if (workspaces.length === 1 && currentTeams?.length === 1) { + setSelectedTeam(currentTeams[0].team_id); + } else { + const lastSelectedTeam = window.localStorage.getItem(LAST_WORSPACE_AND_TEAM) || currentTeams[0].team_id; + const lastSelectedWorkspace = + workspaces.findIndex((workspace) => + workspace.current_teams.find((team) => team.team_id === lastSelectedTeam) + ) || 0; + setSelectedTeam(lastSelectedTeam); + setSelectedWorkspace(lastSelectedWorkspace); + } + + if (workspaces.length === 1 && (currentTeams?.length || 0) <= 1) { + setTimeout(() => { + document.getElementById('continue-to-workspace')?.click(); + }, 100); + } + }, [workspaces]); + const signInToWorkspace = (e: any) => { e.preventDefault(); updateOAuthSession(); @@ -64,7 +91,8 @@ function WorkSpaceScreen() { new Array(3).fill('').forEach((_, i) => { Cookies.remove(`authjs.session-token.${i}`); }); - window && window?.localStorage.removeItem('user-saw-notif'); + window && window?.localStorage.removeItem(USER_SAW_OUTSTANDING_NOTIFICATION); + window && window?.localStorage.setItem(LAST_WORSPACE_AND_TEAM, selectedTeam); }; const updateOAuthSession = useCallback(() => { diff --git a/apps/web/app/constants.ts b/apps/web/app/constants.ts index bc7dd312e..e9ba40c66 100644 --- a/apps/web/app/constants.ts +++ b/apps/web/app/constants.ts @@ -264,6 +264,10 @@ export const languagesFlags = [ } ]; +// Local storage keys +export const LAST_WORSPACE_AND_TEAM = 'last-workspace-and-team'; +export const USER_SAW_OUTSTANDING_NOTIFICATION = 'user-saw-notif'; + // OAuth providers keys export const APPLE_CLIENT_ID = process.env.APPLE_CLIENT_ID; From 2ba0b09d69c8f52ed98bffdc0529bba6f4abe5ad Mon Sep 17 00:00:00 2001 From: AKILIMAILI CIZUNGU Innocent <51681130+Innocent-Akim@users.noreply.github.com> Date: Sat, 6 Jul 2024 17:28:47 +0200 Subject: [PATCH 11/12] [fix]: Tab 'Outstanding' should have (#2687) Estimated time (overall tasks); Total tasks (display how many) --- .../app/hooks/features/useTaskStatistics.ts | 2 +- apps/web/components/ui/select.tsx | 4 +- apps/web/lib/components/dropdown.tsx | 1 - .../web/lib/features/task/daily-plan/index.ts | 4 + .../task/daily-plan/outstanding-all.tsx | 50 ++++ .../task/daily-plan/outstanding-date.tsx | 61 +++++ .../features/task/daily-plan/outstanding.tsx | 62 +---- .../task/daily-plan/task-estimated-count.tsx | 44 ++++ apps/web/lib/features/user-profile-plans.tsx | 91 +++++-- apps/web/package.json | 2 +- yarn.lock | 242 +++++++++++++++--- 11 files changed, 444 insertions(+), 119 deletions(-) create mode 100644 apps/web/lib/features/task/daily-plan/outstanding-all.tsx create mode 100644 apps/web/lib/features/task/daily-plan/outstanding-date.tsx create mode 100644 apps/web/lib/features/task/daily-plan/task-estimated-count.tsx diff --git a/apps/web/app/hooks/features/useTaskStatistics.ts b/apps/web/app/hooks/features/useTaskStatistics.ts index 39c724ebd..f92e64fed 100644 --- a/apps/web/app/hooks/features/useTaskStatistics.ts +++ b/apps/web/app/hooks/features/useTaskStatistics.ts @@ -161,7 +161,7 @@ export function useTaskStatistics(addSeconds = 0) { Math.min( Math.floor( (((_task?.totalWorkedTime || timeSheet?.duration || 0) + addSeconds) * 100) / - (estimate || _task?.estimate || 0) + (estimate || _task?.estimate || 0) ), 100 ), diff --git a/apps/web/components/ui/select.tsx b/apps/web/components/ui/select.tsx index 66bfff203..ac82260e8 100644 --- a/apps/web/components/ui/select.tsx +++ b/apps/web/components/ui/select.tsx @@ -41,7 +41,7 @@ const SelectContent = React.forwardRef< className={cn( 'relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', position === 'popper' && - 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1', + 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1', className )} position={position} @@ -51,7 +51,7 @@ const SelectContent = React.forwardRef< className={cn( 'p-1', position === 'popper' && - 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]' + 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]' )} > {children} diff --git a/apps/web/lib/components/dropdown.tsx b/apps/web/lib/components/dropdown.tsx index 15326d793..fe93d301d 100644 --- a/apps/web/lib/components/dropdown.tsx +++ b/apps/web/lib/components/dropdown.tsx @@ -170,7 +170,6 @@ export function ConfirmDropdown({ return ( {children} - + + {outstandingPlans?.length > 0 ? ( + <> + {outstandingPlans?.map((plan) => ( + <> + {/* */} +
    + {plan?.tasks?.map((task) => { + //If the task is already displayed, skip it + if (displayedTaskId.has(task.id)) { return null; } + // Add the task to the Set to avoid displaying it again + displayedTaskId.add(task.id); + return + } + )} +
+ + ))} + + + ) : ( + + )} + + ); +} diff --git a/apps/web/lib/features/task/daily-plan/outstanding-date.tsx b/apps/web/lib/features/task/daily-plan/outstanding-date.tsx new file mode 100644 index 000000000..ce94aeb5b --- /dev/null +++ b/apps/web/lib/features/task/daily-plan/outstanding-date.tsx @@ -0,0 +1,61 @@ +import { formatDayPlanDate } from '@app/helpers'; +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@components/ui/accordion'; +import { EmptyPlans, PlanHeader } from 'lib/features/user-profile-plans'; +import { TaskCard } from '../task-card'; +import { useDailyPlan } from '@app/hooks'; + +interface IOutstandingFieltreDate { + profile: any +} +export function OutstandingFieltreDate({ profile }: IOutstandingFieltreDate) { + const { outstandingPlans } = useDailyPlan(); + return ( +
+ {outstandingPlans?.length > 0 ? ( + new Date(plan.date).toISOString().split('T')[0])} + > + {outstandingPlans?.map((plan) => ( + + +
+ {formatDayPlanDate(plan.date.toString())} ({plan.tasks?.length}) +
+
+ + {/* Plan header */} + + + {/* Plan tasks list */} +
    + {plan.tasks?.map((task) => ( + + ))} +
+
+
+ ))} +
+ ) : ( + + )} +
+ ); +} diff --git a/apps/web/lib/features/task/daily-plan/outstanding.tsx b/apps/web/lib/features/task/daily-plan/outstanding.tsx index 16ee2ac31..b1b441281 100644 --- a/apps/web/lib/features/task/daily-plan/outstanding.tsx +++ b/apps/web/lib/features/task/daily-plan/outstanding.tsx @@ -1,58 +1,6 @@ -import { formatDayPlanDate } from '@app/helpers'; -import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@components/ui/accordion'; -import { EmptyPlans, PlanHeader } from 'lib/features/user-profile-plans'; -import { TaskCard } from '../task-card'; -import { useDailyPlan } from '@app/hooks'; - -export function Outstanding({ profile }: { profile: any }) { - const { outstandingPlans } = useDailyPlan(); - return ( -
- {outstandingPlans?.length > 0 ? ( - new Date(plan.date).toISOString().split('T')[0])} - > - {outstandingPlans?.map((plan) => ( - - -
- {formatDayPlanDate(plan.date.toString())} ({plan.tasks?.length}) -
-
- - {/* Plan header */} - - - {/* Plan tasks list */} -
    - {plan.tasks?.map((task) => ( - - ))} -
-
-
- ))} -
- ) : ( - - )} -
- ); +interface IOutstanding { + filtre?: any; +} +export function Outstanding({ filtre }: IOutstanding) { + return (<>{filtre}); } diff --git a/apps/web/lib/features/task/daily-plan/task-estimated-count.tsx b/apps/web/lib/features/task/daily-plan/task-estimated-count.tsx new file mode 100644 index 000000000..c7b4de1e2 --- /dev/null +++ b/apps/web/lib/features/task/daily-plan/task-estimated-count.tsx @@ -0,0 +1,44 @@ +import { secondsToTime } from '@app/helpers'; +import { IDailyPlan } from '@app/interfaces'; +import { VerticalSeparator } from 'lib/components' +import React from 'react' + +interface ITaskEstimatedcount { + outstandingPlans: any[] +} +export function TaskEstimatedcount({ outstandingPlans }: ITaskEstimatedcount) { + const element = outstandingPlans?.map((plan: IDailyPlan) => plan.tasks?.map((task) => task)); + const { timesEstemated, totalTasks } = estimatedTotalTime(element || []); + const { h: hour, m: minute } = secondsToTime((timesEstemated || 0)); + return ( +
+
+ Estimated: + {hour}h{minute}m +
+ +
+ Total tasks: + {totalTasks} +
+
+ ) +} + + +export function estimatedTotalTime(data: any) { + // Flatten the data and reduce to calculate the sum of estimates without duplicates + const uniqueTasks = data.flat().reduce((acc: any, task: any) => { + if (!acc[task.id]) { + acc[task.id] = task.estimate; + } + return acc; + }, {}); + + // Calculate the total of estimates + const timesEstemated = Object.values(uniqueTasks)?.reduce((total: number, estimate: any) => total + estimate, 0); + // Calculate the total of tasks + const totalTasks = Object.values(uniqueTasks)?.length; + + return { timesEstemated, totalTasks }; +} diff --git a/apps/web/lib/features/user-profile-plans.tsx b/apps/web/lib/features/user-profile-plans.tsx index 6e169f578..b7c799b7d 100644 --- a/apps/web/lib/features/user-profile-plans.tsx +++ b/apps/web/lib/features/user-profile-plans.tsx @@ -9,14 +9,18 @@ import { Container, NoData, ProgressBar, VerticalSeparator } from 'lib/component import { clsxm } from '@app/utils'; import { fullWidthState } from '@app/stores/fullWidth'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@components/ui/accordion'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@components/ui/select" import { formatDayPlanDate, formatIntegerToHour } from '@app/helpers'; import { EditPenBoxIcon, CheckCircleTickIcon as TickSaveIcon } from 'assets/svg'; import { ReaderIcon, ReloadIcon } from '@radix-ui/react-icons'; -import { Outstanding, PastTasks } from './task/daily-plan'; +import { OutstandingAll, PastTasks, Outstanding, OutstandingFieltreDate } from './task/daily-plan'; import { FutureTasks } from './task/daily-plan/future-tasks'; import { Button } from '@components/ui/button'; +import { IoCalendarOutline } from "react-icons/io5"; + type FilterTabs = 'Today Tasks' | 'Future Tasks' | 'Past Tasks' | 'All Tasks' | 'Outstanding'; +type FiltreOutstanding = 'ALL' | 'DATE'; export function UserProfilePlans() { const defaultTab = @@ -24,57 +28,93 @@ export function UserProfilePlans() { ? (window.localStorage.getItem('daily-plan-tab') as FilterTabs) || null : 'Today Tasks'; + const defaultOutstanding = typeof window !== 'undefined' ? (window.localStorage.getItem('outstanding') as FiltreOutstanding) || null : 'ALL'; + const profile = useUserProfilePage(); const { todayPlan, futurePlans, pastPlans, outstandingPlans, sortedPlans, profileDailyPlans } = useDailyPlan(); const fullWidth = useRecoilValue(fullWidthState); const [currentTab, setCurrentTab] = useState(defaultTab || 'Today Tasks'); + const [currentOutstanding, setCurrentOutstanding] = useState(defaultOutstanding || 'ALL'); + + const screenOutstanding = { + 'ALL': , + "DATE": + } const tabsScreens = { 'Today Tasks': , 'Future Tasks': , 'Past Tasks': , 'All Tasks': , - Outstanding: + Outstanding: }; + + useEffect(() => { window.localStorage.setItem('daily-plan-tab', currentTab); + }, [currentTab]); + useEffect(() => { + window.localStorage.setItem('outstanding', currentOutstanding); + }, [currentOutstanding]); + return (
<> {profileDailyPlans?.items?.length > 0 ? (
-
- {Object.keys(tabsScreens).map((filter, i) => ( -
- {i !== 0 && } -
setCurrentTab(filter as FilterTabs)} - > - {filter} - +
+ {Object.keys(tabsScreens).map((filter, i) => ( +
+ {i !== 0 && } +
setCurrentTab(filter as FilterTabs)} > - {filter === 'Today Tasks' && todayPlan.length} - {filter === 'Future Tasks' && futurePlans.length} - {filter === 'Past Tasks' && pastPlans.length} - {filter === 'All Tasks' && sortedPlans.length} - {filter === 'Outstanding' && outstandingPlans.length} - + {filter} + + {filter === 'Today Tasks' && todayPlan.length} + {filter === 'Future Tasks' && futurePlans.length} + {filter === 'Past Tasks' && pastPlans.length} + {filter === 'All Tasks' && sortedPlans.length} + {filter === 'Outstanding' && outstandingPlans.length} + +
-
- ))} + ))} +
+ {currentTab === 'Outstanding' && ( + + )}
{tabsScreens[currentTab]}
@@ -185,6 +225,7 @@ export function PlanHeader({ plan, planMode }: { plan: IDailyPlan; planMode: Fil // Get all tasks's estimations time const times = plan.tasks?.map((task) => task.estimate).filter((time): time is number => typeof time === 'number') ?? []; + let estimatedTime = 0; if (times.length > 0) estimatedTime = times.reduce((acc, cur) => acc + cur, 0) ?? 0; diff --git a/apps/web/package.json b/apps/web/package.json index b8d842d3c..217d809a3 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -48,7 +48,7 @@ "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-popover": "^1.0.6", "@radix-ui/react-scroll-area": "^1.0.5", - "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-toast": "^1.1.4", "@sentry/nextjs": "^7.80.0", diff --git a/yarn.lock b/yarn.lock index 6b4736944..aefbd5cb1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5471,6 +5471,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/number@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-1.1.0.tgz#1e95610461a09cdf8bb05c152e76ca1278d5da46" + integrity sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ== + "@radix-ui/primitive@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.0.tgz#e1d8ef30b10ea10e69c76e896f608d9276352253" @@ -5485,6 +5490,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/primitive@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.0.tgz#42ef83b3b56dccad5d703ae8c42919a68798bbe2" + integrity sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA== + "@radix-ui/react-accordion@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-accordion/-/react-accordion-1.1.2.tgz#738441f7343e5142273cdef94d12054c3287966f" @@ -5509,6 +5519,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-primitive" "1.0.3" +"@radix-ui/react-arrow@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz#744f388182d360b86285217e43b6c63633f39e7a" + integrity sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw== + dependencies: + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-avatar@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@radix-ui/react-avatar/-/react-avatar-1.0.3.tgz#06a31d06bfb03be5e0d156a5af26a437dce96e7f" @@ -5546,6 +5563,16 @@ "@radix-ui/react-primitive" "1.0.3" "@radix-ui/react-slot" "1.0.2" +"@radix-ui/react-collection@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.1.0.tgz#f18af78e46454a2360d103c2251773028b7724ed" + integrity sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw== + dependencies: + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-slot" "1.1.0" + "@radix-ui/react-compose-refs@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz#37595b1f16ec7f228d698590e78eeed18ff218ae" @@ -5560,6 +5587,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-compose-refs@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz#656432461fc8283d7b591dcf0d79152fae9ecc74" + integrity sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw== + "@radix-ui/react-context@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.0.tgz#f38e30c5859a9fb5e9aa9a9da452ee3ed9e0aee0" @@ -5574,6 +5606,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-context@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.0.tgz#6df8d983546cfd1999c8512f3a8ad85a6e7fcee8" + integrity sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A== + "@radix-ui/react-dialog@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.0.0.tgz#997e97cb183bc90bd888b26b8e23a355ac9fe5f0" @@ -5623,6 +5660,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-direction@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.0.tgz#a7d39855f4d077adc2a1922f9c353c5977a09cdc" + integrity sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg== + "@radix-ui/react-dismissable-layer@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.0.tgz#35b7826fa262fd84370faef310e627161dffa76b" @@ -5659,6 +5701,17 @@ "@radix-ui/react-use-callback-ref" "1.0.1" "@radix-ui/react-use-escape-keydown" "1.0.3" +"@radix-ui/react-dismissable-layer@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.0.tgz#2cd0a49a732372513733754e6032d3fb7988834e" + integrity sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig== + dependencies: + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-escape-keydown" "1.1.0" + "@radix-ui/react-dropdown-menu@^2.0.6": version "2.0.6" resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz#cdf13c956c5e263afe4e5f3587b3071a25755b63" @@ -5687,6 +5740,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-focus-guards@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz#8e9abb472a9a394f59a1b45f3dd26cfe3fc6da13" + integrity sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw== + "@radix-ui/react-focus-scope@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.0.tgz#95a0c1188276dc8933b1eac5f1cdb6471e01ade5" @@ -5717,6 +5775,15 @@ "@radix-ui/react-primitive" "1.0.3" "@radix-ui/react-use-callback-ref" "1.0.1" +"@radix-ui/react-focus-scope@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz#ebe2891a298e0a33ad34daab2aad8dea31caf0b2" + integrity sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA== + dependencies: + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-hover-card@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@radix-ui/react-hover-card/-/react-hover-card-1.0.6.tgz#ea4b5c02e9f145b278cbcacaf5200f80d44cba22" @@ -5754,6 +5821,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-use-layout-effect" "1.0.1" +"@radix-ui/react-id@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.0.tgz#de47339656594ad722eb87f94a6b25f9cffae0ed" + integrity sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA== + dependencies: + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-menu@2.0.6": version "2.0.6" resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.0.6.tgz#2c9e093c1a5d5daa87304b2a2f884e32288ae79e" @@ -5835,6 +5909,22 @@ "@radix-ui/react-use-size" "1.0.1" "@radix-ui/rect" "1.0.1" +"@radix-ui/react-popper@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.0.tgz#a3e500193d144fe2d8f5d5e60e393d64111f2a7a" + integrity sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg== + dependencies: + "@floating-ui/react-dom" "^2.0.0" + "@radix-ui/react-arrow" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-use-rect" "1.1.0" + "@radix-ui/react-use-size" "1.1.0" + "@radix-ui/rect" "1.1.0" + "@radix-ui/react-portal@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.0.tgz#7220b66743394fabb50c55cb32381395cc4a276b" @@ -5859,6 +5949,14 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-primitive" "1.0.3" +"@radix-ui/react-portal@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.1.tgz#1957f1eb2e1aedfb4a5475bd6867d67b50b1d15f" + integrity sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g== + dependencies: + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-presence@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.0.tgz#814fe46df11f9a468808a6010e3f3ca7e0b2e84a" @@ -5893,6 +5991,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-slot" "1.0.2" +"@radix-ui/react-primitive@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz#fe05715faa9203a223ccc0be15dc44b9f9822884" + integrity sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw== + dependencies: + "@radix-ui/react-slot" "1.1.0" + "@radix-ui/react-roving-focus@1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz#e90c4a6a5f6ac09d3b8c1f5b5e81aab2f0db1974" @@ -5925,33 +6030,32 @@ "@radix-ui/react-use-callback-ref" "1.0.1" "@radix-ui/react-use-layout-effect" "1.0.1" -"@radix-ui/react-select@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-2.0.0.tgz#a3511792a51a7018d6559357323a7f52e0e38887" - integrity sha512-RH5b7af4oHtkcHS7pG6Sgv5rk5Wxa7XI8W5gvB1N/yiuDGZxko1ynvOiVhFM7Cis2A8zxF9bTOUVbRDzPepe6w== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/number" "1.0.1" - "@radix-ui/primitive" "1.0.1" - "@radix-ui/react-collection" "1.0.3" - "@radix-ui/react-compose-refs" "1.0.1" - "@radix-ui/react-context" "1.0.1" - "@radix-ui/react-direction" "1.0.1" - "@radix-ui/react-dismissable-layer" "1.0.5" - "@radix-ui/react-focus-guards" "1.0.1" - "@radix-ui/react-focus-scope" "1.0.4" - "@radix-ui/react-id" "1.0.1" - "@radix-ui/react-popper" "1.1.3" - "@radix-ui/react-portal" "1.0.4" - "@radix-ui/react-primitive" "1.0.3" - "@radix-ui/react-slot" "1.0.2" - "@radix-ui/react-use-callback-ref" "1.0.1" - "@radix-ui/react-use-controllable-state" "1.0.1" - "@radix-ui/react-use-layout-effect" "1.0.1" - "@radix-ui/react-use-previous" "1.0.1" - "@radix-ui/react-visually-hidden" "1.0.3" +"@radix-ui/react-select@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-2.1.1.tgz#df05cb0b29d3deaef83b505917c4042e0e418a9f" + integrity sha512-8iRDfyLtzxlprOo9IicnzvpsO1wNCkuwzzCM+Z5Rb5tNOpCdMvcc2AkzX0Fz+Tz9v6NJ5B/7EEgyZveo4FBRfQ== + dependencies: + "@radix-ui/number" "1.1.0" + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-collection" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.0" + "@radix-ui/react-direction" "1.1.0" + "@radix-ui/react-dismissable-layer" "1.1.0" + "@radix-ui/react-focus-guards" "1.1.0" + "@radix-ui/react-focus-scope" "1.1.0" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-popper" "1.2.0" + "@radix-ui/react-portal" "1.1.1" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-slot" "1.1.0" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-use-previous" "1.1.0" + "@radix-ui/react-visually-hidden" "1.1.0" aria-hidden "^1.1.1" - react-remove-scroll "2.5.5" + react-remove-scroll "2.5.7" "@radix-ui/react-slot@1.0.0": version "1.0.0" @@ -5969,6 +6073,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-compose-refs" "1.0.1" +"@radix-ui/react-slot@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.0.tgz#7c5e48c36ef5496d97b08f1357bb26ed7c714b84" + integrity sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw== + dependencies: + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-toast@^1.1.4": version "1.1.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.1.4.tgz#9a7fc2d71700886f3292f7699c905f1e01be59e1" @@ -6002,6 +6113,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-use-callback-ref@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1" + integrity sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw== + "@radix-ui/react-use-controllable-state@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz#a64deaafbbc52d5d407afaa22d493d687c538b7f" @@ -6018,6 +6134,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-use-callback-ref" "1.0.1" +"@radix-ui/react-use-controllable-state@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz#1321446857bb786917df54c0d4d084877aab04b0" + integrity sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw== + dependencies: + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-escape-keydown@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.0.tgz#aef375db4736b9de38a5a679f6f49b45a060e5d1" @@ -6034,6 +6157,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-use-callback-ref" "1.0.1" +"@radix-ui/react-use-escape-keydown@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz#31a5b87c3b726504b74e05dac1edce7437b98754" + integrity sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw== + dependencies: + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-layout-effect@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz#2fc19e97223a81de64cd3ba1dc42ceffd82374dc" @@ -6048,12 +6178,15 @@ dependencies: "@babel/runtime" "^7.13.10" -"@radix-ui/react-use-previous@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz#b595c087b07317a4f143696c6a01de43b0d0ec66" - integrity sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw== - dependencies: - "@babel/runtime" "^7.13.10" +"@radix-ui/react-use-layout-effect@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz#3c2c8ce04827b26a39e442ff4888d9212268bd27" + integrity sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w== + +"@radix-ui/react-use-previous@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz#d4dd37b05520f1d996a384eb469320c2ada8377c" + integrity sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og== "@radix-ui/react-use-rect@1.0.1": version "1.0.1" @@ -6063,6 +6196,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/rect" "1.0.1" +"@radix-ui/react-use-rect@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz#13b25b913bd3e3987cc9b073a1a164bb1cf47b88" + integrity sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ== + dependencies: + "@radix-ui/rect" "1.1.0" + "@radix-ui/react-use-size@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz#1c5f5fea940a7d7ade77694bb98116fb49f870b2" @@ -6071,6 +6211,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-use-layout-effect" "1.0.1" +"@radix-ui/react-use-size@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz#b4dba7fbd3882ee09e8d2a44a3eed3a7e555246b" + integrity sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw== + dependencies: + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-visually-hidden@1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz#51aed9dd0fe5abcad7dee2a234ad36106a6984ac" @@ -6079,6 +6226,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-primitive" "1.0.3" +"@radix-ui/react-visually-hidden@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz#ad47a8572580f7034b3807c8e6740cd41038a5a2" + integrity sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ== + dependencies: + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/rect@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.0.1.tgz#bf8e7d947671996da2e30f4904ece343bc4a883f" @@ -6086,6 +6240,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/rect@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.1.0.tgz#f817d1d3265ac5415dadc67edab30ae196696438" + integrity sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg== + "@remix-run/router@1.16.1": version "1.16.1" resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.16.1.tgz#73db3c48b975eeb06d0006481bde4f5f2d17d1cd" @@ -21701,6 +21860,14 @@ react-remove-scroll-bar@^2.3.3: react-style-singleton "^2.2.1" tslib "^2.0.0" +react-remove-scroll-bar@^2.3.4: + version "2.3.6" + resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz#3e585e9d163be84a010180b18721e851ac81a29c" + integrity sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g== + dependencies: + react-style-singleton "^2.2.1" + tslib "^2.0.0" + react-remove-scroll@2.5.4: version "2.5.4" resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.4.tgz#afe6491acabde26f628f844b67647645488d2ea0" @@ -21723,6 +21890,17 @@ react-remove-scroll@2.5.5: use-callback-ref "^1.3.0" use-sidecar "^1.1.2" +react-remove-scroll@2.5.7: + version "2.5.7" + resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz#15a1fd038e8497f65a695bf26a4a57970cac1ccb" + integrity sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA== + dependencies: + react-remove-scroll-bar "^2.3.4" + react-style-singleton "^2.2.1" + tslib "^2.1.0" + use-callback-ref "^1.3.0" + use-sidecar "^1.1.2" + react-resizable-panels@^2.0.19: version "2.0.19" resolved "https://registry.yarnpkg.com/react-resizable-panels/-/react-resizable-panels-2.0.19.tgz#df259898c682cb774af65c3bc38c1b29c855b99b" From 27df0e03a5c5b3d0f134b3576e47584b5b39a5fb Mon Sep 17 00:00:00 2001 From: syns Date: Mon, 8 Jul 2024 04:16:08 +0700 Subject: [PATCH 12/12] [Fix] server web on windows (#2688) * fix: translation folder name * fix: update setting * fix: server web setting page * feat: auto updater * feat: setting page blank on device windows * fix: server web starting notify * fix: auto check update server web * chore: added radixui switch module * fix: case missing break * style: styling code --- apps/server-web/package.json | 4 +- .../server-web/src/configs/i18n.mainconfig.ts | 4 +- apps/server-web/src/configs/i18nResource.ts | 7 +- .../src/locales/bg/translation.missing.json | 1 - .../src/locales/en/translation.json | 38 ---- .../src/locales/en/translation.missing.json | 6 - .../locales/{ => i18n}/bg/translation.json | 20 +- .../locales/i18n/bg/translation.missing.json | 15 ++ .../src/locales/i18n/en/translation.json | 61 ++++++ .../locales/i18n/en/translation.missing.json | 19 ++ apps/server-web/src/main/helpers/constant.ts | 59 ++--- .../src/main/helpers/desktop-server.ts | 2 +- .../src/main/helpers/interfaces/i-server.ts | 4 +- .../helpers/services/libs/desktop-store.ts | 8 +- .../main/helpers/services/libs/server-task.ts | 2 +- .../src/main/helpers/services/web-service.ts | 2 +- apps/server-web/src/main/main.ts | 145 ++++++++----- apps/server-web/src/main/updater.ts | 92 +++++--- apps/server-web/src/main/util.ts | 10 +- apps/server-web/src/main/windows/dialog.ts | 34 +++ apps/server-web/src/renderer/App.css | 141 +++++++++++- .../src/renderer/components/About.tsx | 2 +- .../src/renderer/components/Select.tsx | 85 ++++++++ .../src/renderer/components/Server.tsx | 131 ++++++----- .../src/renderer/components/SideBar.tsx | 83 ++++++- .../src/renderer/components/Toast.tsx | 59 +++++ .../src/renderer/components/Updater.tsx | 205 +++++++++++++++--- .../server-web/src/renderer/pages/Setting.tsx | 79 +++++-- apps/server-web/tailwind.config.js | 17 ++ yarn.lock | 18 ++ 30 files changed, 1051 insertions(+), 302 deletions(-) delete mode 100644 apps/server-web/src/locales/bg/translation.missing.json delete mode 100644 apps/server-web/src/locales/en/translation.json delete mode 100644 apps/server-web/src/locales/en/translation.missing.json rename apps/server-web/src/locales/{ => i18n}/bg/translation.json (50%) create mode 100644 apps/server-web/src/locales/i18n/bg/translation.missing.json create mode 100644 apps/server-web/src/locales/i18n/en/translation.json create mode 100644 apps/server-web/src/locales/i18n/en/translation.missing.json create mode 100644 apps/server-web/src/main/windows/dialog.ts create mode 100644 apps/server-web/src/renderer/components/Select.tsx create mode 100644 apps/server-web/src/renderer/components/Toast.tsx diff --git a/apps/server-web/package.json b/apps/server-web/package.json index cf5782d0e..626d9bcd5 100644 --- a/apps/server-web/package.json +++ b/apps/server-web/package.json @@ -49,7 +49,9 @@ "i18next-electron-fs-backend": "^3.0.1", "i18next-fs-backend": "^2.3.1", "i18next-resources-to-backend": "^1.2.1", - "react-i18next": "^14.1.0" + "react-i18next": "^14.1.0", + "@radix-ui/react-switch": "^1.1.0", + "classnames": "^2.5.1" }, "devDependencies": { "electron": "28.1.0", diff --git a/apps/server-web/src/configs/i18n.mainconfig.ts b/apps/server-web/src/configs/i18n.mainconfig.ts index e01d9d288..d7814fe6c 100644 --- a/apps/server-web/src/configs/i18n.mainconfig.ts +++ b/apps/server-web/src/configs/i18n.mainconfig.ts @@ -9,8 +9,8 @@ const prependPath = app.isPackaged i18n.use(backend).init({ backend: { - loadPath: prependPath + '/src/locales/{{lng}}/{{ns}}.json', - addPath: prependPath + '/src/locales/{{lng}}/{{ns}}.missing.json' + loadPath: prependPath + '/src/locales/i18n/{{lng}}/{{ns}}.json', + addPath: prependPath + '/src/locales/i18n/{{lng}}/{{ns}}.missing.json' }, debug: false, ns: 'translation', diff --git a/apps/server-web/src/configs/i18nResource.ts b/apps/server-web/src/configs/i18nResource.ts index f093848d4..5f4b14f98 100644 --- a/apps/server-web/src/configs/i18nResource.ts +++ b/apps/server-web/src/configs/i18nResource.ts @@ -3,7 +3,7 @@ import resourcesToBackend from 'i18next-resources-to-backend'; i18n .use( - resourcesToBackend((language: string, namespace: string) => import(`../locales/${language}/${namespace}.json`)) + resourcesToBackend((language: string, namespace: string) => import(`../locales/i18n/${language}/${namespace}.json`)) ) .init({ debug: true, @@ -13,6 +13,9 @@ i18n } }); i18n.on('failedLoading', (lng, ns, msg) => console.error(msg)) -i18n.languages = ['en', 'bg']; +i18n.languages = [ + 'en', + 'bg', +]; export default i18n; diff --git a/apps/server-web/src/locales/bg/translation.missing.json b/apps/server-web/src/locales/bg/translation.missing.json deleted file mode 100644 index 0967ef424..000000000 --- a/apps/server-web/src/locales/bg/translation.missing.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/apps/server-web/src/locales/en/translation.json b/apps/server-web/src/locales/en/translation.json deleted file mode 100644 index e91511fb9..000000000 --- a/apps/server-web/src/locales/en/translation.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "MENU": { - "SERVER": "Server", - "UPDATER": "Updater", - "ABOUT": "About", - "SERVER_START": "Start", - "SERVER_STOP": "Stop", - "APP_SETTING": "Setting", - "APP_ABOUT": "About", - "APP_QUIT": "Quit", - "GENERAL": "General", - "SERVER_STATUS_STOPPED": "Status: Stopped", - "SERVER_STATUS_STARTED": "Status: Started" - }, - "FORM": { - "FIELDS": { - "PORT": "PORT", - "GAUZY_API_SERVER_URL": "Gauzy API Server Url", - "NEXT_PUBLIC_GAUZY_API_SERVER_URL": "Public Gauzy API Server Url" - }, - "BUTTON": { - "SAVE_SETTING": "Save Setting", - "OK": "OK" - }, - "LABELS": { - "CHECKING": "Checking", - "DOWNLOADING": "Downloading", - "QUIT_N_INSTALL": "Quit and Install", - "UP_TO_DATE": "Up to date", - "UPDATE_AVAILABLE": "Update Available", - "CHECK_FOR_UPDATE": "Check For Update" - } - }, - "MESSAGE": { - "SUCCESS": "Success", - "ERROR": "Error" - } -} diff --git a/apps/server-web/src/locales/en/translation.missing.json b/apps/server-web/src/locales/en/translation.missing.json deleted file mode 100644 index eca021d2c..000000000 --- a/apps/server-web/src/locales/en/translation.missing.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "MENU": { - "STATUS": "MENU.STATUS", - "STOPPED": "MENU.STOPPED" - } -} \ No newline at end of file diff --git a/apps/server-web/src/locales/bg/translation.json b/apps/server-web/src/locales/i18n/bg/translation.json similarity index 50% rename from apps/server-web/src/locales/bg/translation.json rename to apps/server-web/src/locales/i18n/bg/translation.json index 30d6df466..95b7f55a2 100644 --- a/apps/server-web/src/locales/bg/translation.json +++ b/apps/server-web/src/locales/i18n/bg/translation.json @@ -1,7 +1,7 @@ { "MENU": { "SERVER": "сървър", - "UPDATER": "Актуализатор", + "UPDATER": "Актуализация", "ABOUT": "относно", "SERVER_START": "Започнете", "SERVER_STOP": "Спри се", @@ -16,7 +16,9 @@ "FIELDS": { "PORT": "ПРИСТАНИЩЕ", "GAUZY_API_SERVER_URL": "Gauzy API сървър Url", - "NEXT_PUBLIC_GAUZY_API_SERVER_URL": "Публичен Gauzy API сървър Url" + "NEXT_PUBLIC_GAUZY_API_SERVER_URL": "Публичен Gauzy API сървър Url", + "LANGUAGES": "Езици", + "OPTIONS": "Настроики" }, "BUTTON": { "SAVE_SETTING": "Запазване на настройката", @@ -28,7 +30,19 @@ "QUIT_N_INSTALL": "Излезте и инсталирайте", "UP_TO_DATE": "Актуална", "UPDATE_AVAILABLE": "Налична актуализация", - "CHECK_FOR_UPDATE": "Проверка за актуализация" + "CHECK_FOR_UPDATE": "Проверка за актуализация", + "SERVER_CONFIG": "Конфигурация на сървъра", + "UPDATE_OPTIONS": { + "A_DAY": "Ден", + "A_HOURS": "A часа", + "3_HOURS": "3 часа", + "30_MINUTES": "30 минути" + }, + "AUTO_UPDATE_TITLE": "Автоматична проверка на актуализацията", + "AUTO_UPDATE_SUBTITLE": "Активирайте автоматичната проверка на актуализацията, за да стартирате заявка за проверка дали е налична нова версия и да уведомите", + "AUTO_UPDATE_TOGLE": "Автоматична актуализация", + "CHECK_UPDATE_TITLE": "Проверете и актуализирайте версията на приложението си", + "CHECK_UPDATE_SUBTITLE": "Налична е нова актуализация! Моля, щракнете върху бутона Изтегляне сега по-долу." } }, "MESSAGE": { diff --git a/apps/server-web/src/locales/i18n/bg/translation.missing.json b/apps/server-web/src/locales/i18n/bg/translation.missing.json new file mode 100644 index 000000000..501f9fbf2 --- /dev/null +++ b/apps/server-web/src/locales/i18n/bg/translation.missing.json @@ -0,0 +1,15 @@ +{ + "Status: Started": "Status: Started", + "Start": "Start", + "Stop": "Stop", + "Setting": "Setting", + "About": "About", + "Quit": "Quit", + "Статус: Спряна": "Статус: Спряна", + "Започнете": "Започнете", + "Спри се": "Спри се", + "Настройка": "Настройка", + "относно": "относно", + "Откажете се": "Откажете се", + "Status: Stopped": "Status: Stopped" +} \ No newline at end of file diff --git a/apps/server-web/src/locales/i18n/en/translation.json b/apps/server-web/src/locales/i18n/en/translation.json new file mode 100644 index 000000000..2e9d94f58 --- /dev/null +++ b/apps/server-web/src/locales/i18n/en/translation.json @@ -0,0 +1,61 @@ +{ + "MENU": { + "SERVER": "Server", + "UPDATER": "Update", + "ABOUT": "About", + "SERVER_START": "Start", + "SERVER_STOP": "Stop", + "APP_SETTING": "Setting", + "APP_ABOUT": "About", + "APP_QUIT": "Quit", + "GENERAL": "General", + "SERVER_STATUS_STOPPED": "Status: Stopped", + "SERVER_STATUS_STARTED": "Status: Started" + }, + "FORM": { + "FIELDS": { + "PORT": "PORT", + "GAUZY_API_SERVER_URL": "Gauzy API Server Url", + "NEXT_PUBLIC_GAUZY_API_SERVER_URL": "Public Gauzy API Server Url", + "LANGUAGES": "Languages", + "OPTIONS": "Options" + }, + "BUTTON": { + "SAVE_SETTING": "Save Setting", + "OK": "OK", + "YES": "Yes", + "NO": "No", + "DOWNLOAD_NOW": "Download Now", + "LATER": "Later", + "CANCEL": "Cancel" + }, + "LABELS": { + "CHECKING": "Checking", + "DOWNLOADING": "Downloading", + "QUIT_N_INSTALL": "Quit and Install", + "UP_TO_DATE": "Up to date", + "UPDATE_AVAILABLE": "Update Available", + "CHECK_FOR_UPDATE": "Check For Update", + "SERVER_CONFIG": "Server Configuration", + "UPDATE_OPTIONS": { + "A_DAY": "A Day", + "A_HOURS": "A Hours", + "3_HOURS": "30 Hours", + "30_MINUTES": "30 Minutes" + }, + "AUTO_UPDATE_TITLE": "Automatic Update Check", + "AUTO_UPDATE_SUBTITLE": "Enable automatice update check, in order to run a request to check if new version is available and notify", + "AUTO_UPDATE_TOGLE": "Automatic Update", + "CHECK_UPDATE_TITLE": "Check & Update your app version", + "CHECK_UPDATE_SUBTITLE": "New Update is available! Please click button Download Now below." + } + }, + "MESSAGE": { + "SUCCESS": "Success", + "ERROR": "Error", + "WARNING": "Warning", + "INFO": "Info", + "UPDATE_AVAILABLE": "New Update is available! Please click button Download Now below.", + "EXIT_MESSAGE": "Server web still running, Are you sure to exit the app ?" + } +} diff --git a/apps/server-web/src/locales/i18n/en/translation.missing.json b/apps/server-web/src/locales/i18n/en/translation.missing.json new file mode 100644 index 000000000..6da3e54d9 --- /dev/null +++ b/apps/server-web/src/locales/i18n/en/translation.missing.json @@ -0,0 +1,19 @@ +{ + "MENU": { + "STATUS": "MENU.STATUS", + "STOPPED": "MENU.STOPPED" + }, + "SERVER_STATUS_STOPPED": "SERVER_STATUS_STOPPED", + "Статус: Спряна": "Статус: Спряна", + "Започнете": "Започнете", + "Спри се": "Спри се", + "Настройка": "Настройка", + "относно": "относно", + "Откажете се": "Откажете се", + "Status: Stopped": "Status: Stopped", + "Start": "Start", + "Stop": "Stop", + "Setting": "Setting", + "About": "About", + "Quit": "Quit" +} \ No newline at end of file diff --git a/apps/server-web/src/main/helpers/constant.ts b/apps/server-web/src/main/helpers/constant.ts index aef8a0245..cab7ffd1e 100644 --- a/apps/server-web/src/main/helpers/constant.ts +++ b/apps/server-web/src/main/helpers/constant.ts @@ -1,31 +1,34 @@ export const EventLists = { - webServerStarted: 'WEB_SERVER_STARTED', - webServerStopped: 'WEB_SERVER_STOPPED', - webServerStart: 'WEB_SERVER_START', - webServerStop: 'WEB_SERVER_STOP', - gotoSetting: 'GO_TO_SETTING', - gotoAbout: 'GO_TO_ABOUT', - UPDATE_AVAILABLE: 'UPDATE_AVAILABLE', - UPDATE_ERROR: 'UPDATE_ERROR', - UPDATE_NOT_AVAILABLE: 'UPDATE_NOT_AVAILABLE', - UPDATE_PROGRESS: 'UPDATE_PROGRESS', - UPDATE_DOWNLOADED: 'UPDATE_DOWNLOADED', - UPDATE_CANCELLED: 'UPDATE_CANCELLED', - CHANGE_LANGUAGE: 'CHANGE_LANGUAGE' - } + webServerStarted: 'WEB_SERVER_STARTED', + webServerStopped: 'WEB_SERVER_STOPPED', + webServerStart: 'WEB_SERVER_START', + webServerStop: 'WEB_SERVER_STOP', + gotoSetting: 'GO_TO_SETTING', + gotoAbout: 'GO_TO_ABOUT', + UPDATE_AVAILABLE: 'UPDATE_AVAILABLE', + UPDATE_ERROR: 'UPDATE_ERROR', + UPDATE_NOT_AVAILABLE: 'UPDATE_NOT_AVAILABLE', + UPDATE_PROGRESS: 'UPDATE_PROGRESS', + UPDATE_DOWNLOADED: 'UPDATE_DOWNLOADED', + UPDATE_CANCELLED: 'UPDATE_CANCELLED', + CHANGE_LANGUAGE: 'CHANGE_LANGUAGE' +} - export const SettingPageTypeMessage = { - loadSetting: 'load-setting', - checkUpdate: 'check-for-update', - updateAvailable: 'update-available', - downloadingUpdate: 'downloading-update', - downloaded: 'downloaded-update', - installUpdate: 'install-update', - saveSetting: 'save-setting', - updateError: 'update-error', - upToDate: 'up-to-date', - mainResponse: 'main-response', - showVersion: 'show-version', - selectMenu: 'select-menu', - langChange: 'lang' +export const SettingPageTypeMessage = { + loadSetting: 'load-setting', + checkUpdate: 'check-for-update', + updateAvailable: 'update-available', + downloadingUpdate: 'downloading-update', + downloaded: 'downloaded-update', + installUpdate: 'install-update', + saveSetting: 'save-setting', + updateError: 'update-error', + upToDate: 'up-to-date', + mainResponse: 'main-response', + showVersion: 'show-version', + selectMenu: 'select-menu', + langChange: 'lang', + updateSetting: 'update-setting', + updateSettingResponse: 'update-setting-response', + updateCancel: 'update-cancel' } diff --git a/apps/server-web/src/main/helpers/desktop-server.ts b/apps/server-web/src/main/helpers/desktop-server.ts index fc367e6a1..0788f3816 100644 --- a/apps/server-web/src/main/helpers/desktop-server.ts +++ b/apps/server-web/src/main/helpers/desktop-server.ts @@ -16,7 +16,7 @@ export enum ServerState { export class DesktopServer { private state: ServerState = ServerState.STOPPED; private stateObserver: Observer; - private eventEmitter:EventEmitter; + private eventEmitter: EventEmitter; constructor(private readonly isOnlyApiServer = false, eventEmitter: EventEmitter) { // super(); diff --git a/apps/server-web/src/main/helpers/interfaces/i-server.ts b/apps/server-web/src/main/helpers/interfaces/i-server.ts index c3a27f7b7..adb236d32 100644 --- a/apps/server-web/src/main/helpers/interfaces/i-server.ts +++ b/apps/server-web/src/main/helpers/interfaces/i-server.ts @@ -1,5 +1,7 @@ interface GeneralConfig { - lang: string + lang?: string + autoUpdate?: boolean + updateCheckPeriode?: string [key: string]: any } diff --git a/apps/server-web/src/main/helpers/services/libs/desktop-store.ts b/apps/server-web/src/main/helpers/services/libs/desktop-store.ts index 68d2f71f7..18c14ba73 100644 --- a/apps/server-web/src/main/helpers/services/libs/desktop-store.ts +++ b/apps/server-web/src/main/helpers/services/libs/desktop-store.ts @@ -10,11 +10,11 @@ export const LocalStore = { let config: WebServer | any = store.get('config'); Object.keys(values).forEach((key: string) => { if (key === 'server') { - config[key] = {...config[key], ...values.server } + config[key] = { ...config[key], ...values.server } } if (key === 'general') { - config[key] = {...config[key], ...values.general } + config[key] = { ...config[key], ...values.general } } }) store.set({ @@ -33,7 +33,9 @@ export const LocalStore = { NEXT_PUBLIC_GAUZY_API_SERVER_URL: 'http://localhost:3000' }, general: { - lang: 'en' + lang: 'en', + autoUpdate: true, + updateCheckPeriode: '30' } } store.set({ config }); diff --git a/apps/server-web/src/main/helpers/services/libs/server-task.ts b/apps/server-web/src/main/helpers/services/libs/server-task.ts index b3887f789..370cdb746 100644 --- a/apps/server-web/src/main/helpers/services/libs/server-task.ts +++ b/apps/server-web/src/main/helpers/services/libs/server-task.ts @@ -111,7 +111,7 @@ export abstract class ServerTask { if (this.eventEmmitter) { this.eventEmmitter.emit(EventLists.webServerStarted); } - this.config.setting = { server: { ...this.config.setting.server ,[this.pid]: service.pid } }; + this.config.setting = { server: { ...this.config.setting.server, [this.pid]: service.pid } }; } catch (error) { console.error('Error running task:', error); this.handleError(error); diff --git a/apps/server-web/src/main/helpers/services/web-service.ts b/apps/server-web/src/main/helpers/services/web-service.ts index 62bff4df2..80d02a4ff 100644 --- a/apps/server-web/src/main/helpers/services/web-service.ts +++ b/apps/server-web/src/main/helpers/services/web-service.ts @@ -13,7 +13,7 @@ export class WebService extends ServerTask { const args = { ...env, serviceName: 'WebServer' }; // Note: do not change this prefix because we may use it to detect the success message from the running server! - const successMessage = 'Listening at http'; + const successMessage = 'Starting...'; const errorMessage = 'Error running API server:'; diff --git a/apps/server-web/src/main/main.ts b/apps/server-web/src/main/main.ts index a8dfd1205..c3b1cadfc 100644 --- a/apps/server-web/src/main/main.ts +++ b/apps/server-web/src/main/main.ts @@ -23,10 +23,10 @@ const isProd = process.env.NODE_ENV === 'production'; // const appPath = app.getAppPath(); let isServerRun: boolean; - -let tray:Tray; +let intervalUpdate: NodeJS.Timeout; +let tray: Tray; let settingWindow: BrowserWindow | null = null; -const updater = new Updater(eventEmitter); +const updater = new Updater(eventEmitter, i18nextMainBackend); i18nextMainBackend.on('initialized', () => { const config = LocalStore.getStore('config'); const selectedLang = config && config.general && config.general.lang; @@ -37,8 +37,8 @@ i18nextMainBackend.on('initialized', () => { let trayMenuItems: any = []; const RESOURCES_PATH = app.isPackaged - ? path.join(process.resourcesPath, 'assets/icons/gauzy') - : path.join(__dirname, '../../assets/icons/gauzy'); + ? path.join(process.resourcesPath, 'assets/icons/gauzy') + : path.join(__dirname, '../../assets/icons/gauzy'); const getAssetPath = (...paths: string[]): string => { return path.join(RESOURCES_PATH, ...paths); @@ -113,7 +113,7 @@ const createWindow = async () => { const url = resolveHtmlPath('index.html', 'setting'); settingWindow.loadURL(url); - mainBindings(ipcMain, settingWindow , fs); + mainBindings(ipcMain, settingWindow, fs); settingWindow.on('closed', () => { settingWindow = null; }); @@ -121,32 +121,32 @@ const createWindow = async () => { }; const runServer = async () => { - console.log('Run the Server...'); - try { - const envVal = getEnvApi(); - - // Instantiate API and UI servers - await desktopServer.start( - { api: serverPath }, - envVal, - undefined, - signal - ); - } catch (error:any) { - if (error.name === 'AbortError') { - console.log('You exit without to stop the server'); - return; - } - } + console.log('Run the Server...'); + try { + const envVal = getEnvApi(); + + // Instantiate API and UI servers + await desktopServer.start( + { api: serverPath }, + envVal, + undefined, + signal + ); + } catch (error: any) { + if (error.name === 'AbortError') { + console.log('You exit without to stop the server'); + return; + } + } }; const stopServer = async () => { - await desktopServer.stop(); + await desktopServer.stop(); }; const getEnvApi = () => { const setting: WebServer = LocalStore.getStore('config') - return setting.server; + return setting.server; }; const SendMessageToSettingWindow = (type: string, data: any) => { @@ -158,6 +158,7 @@ const SendMessageToSettingWindow = (type: string, data: any) => { const onInitApplication = () => { LocalStore.setDefaultServerConfig(); // check and set default config + createIntervalAutoUpdate() trayMenuItems = trayMenuItems.length ? trayMenuItems : defaultTrayMenuItem(eventEmitter); tray = _initTray(trayMenuItems, getAssetPath('icon.png')); i18nextMainBackend.on('languageChanged', (lng) => { @@ -201,47 +202,50 @@ const onInitApplication = () => { console.log('setting data', serverSetting); settingWindow?.show(); settingWindow?.webContents.once('did-finish-load', () => { - setTimeout(()=> { + setTimeout(() => { settingWindow?.webContents.send('languageSignal', serverSetting.general?.lang); SendMessageToSettingWindow(SettingPageTypeMessage.loadSetting, serverSetting); }, 50) }) }) - eventEmitter.on(EventLists.UPDATE_AVAILABLE, (data)=> { + eventEmitter.on(EventLists.UPDATE_AVAILABLE, (data) => { console.log('UPDATE_AVAILABLE', data); SendMessageToSettingWindow(SettingPageTypeMessage.updateAvailable, data); }) - eventEmitter.on(EventLists.UPDATE_ERROR, (data)=> { + eventEmitter.on(EventLists.UPDATE_ERROR, (data) => { console.log('UPDATE_ERROR', data); - SendMessageToSettingWindow(SettingPageTypeMessage.updateError, {message: JSON.stringify(data)}); + SendMessageToSettingWindow(SettingPageTypeMessage.updateError, { message: JSON.stringify(data) }); }) - eventEmitter.on(EventLists.UPDATE_NOT_AVAILABLE, (data)=> { + eventEmitter.on(EventLists.UPDATE_NOT_AVAILABLE, (data) => { console.log('UPDATE_NOT_AVAILABLE', data); SendMessageToSettingWindow(SettingPageTypeMessage.upToDate, data); }) - eventEmitter.on(EventLists.UPDATE_PROGRESS, (data)=> { + eventEmitter.on(EventLists.UPDATE_PROGRESS, (data) => { console.log('UPDATE_PROGRESS', data.percent); - SendMessageToSettingWindow(SettingPageTypeMessage.downloadingUpdate, {percent: Math.floor(data.percent || 0)}); + SendMessageToSettingWindow(SettingPageTypeMessage.downloadingUpdate, { percent: Math.floor(data.percent || 0) }); }) - eventEmitter.on(EventLists.UPDATE_DOWNLOADED, (data)=> { + eventEmitter.on(EventLists.UPDATE_DOWNLOADED, (data) => { console.log('UPDATE_DOWNLOADED', data); SendMessageToSettingWindow(SettingPageTypeMessage.downloaded, data); }) - eventEmitter.on(EventLists.UPDATE_CANCELLED, (data)=> { + eventEmitter.on(EventLists.UPDATE_CANCELLED, (data) => { console.log('UPDATE_CANCELLED', data); + SendMessageToSettingWindow(SettingPageTypeMessage.updateCancel, data); }) eventEmitter.on(EventLists.CHANGE_LANGUAGE, (data) => { i18nextMainBackend.changeLanguage(data.code); - LocalStore.updateConfigSetting({general: { - lang: data.code - }}) + LocalStore.updateConfigSetting({ + general: { + lang: data.code + } + }) }) eventEmitter.on(EventLists.gotoAbout, async () => { @@ -251,16 +255,16 @@ const onInitApplication = () => { const serverSetting = LocalStore.getStore('config'); settingWindow?.show(); settingWindow?.webContents.once('did-finish-load', () => { - setTimeout(()=> { + setTimeout(() => { SendMessageToSettingWindow(SettingPageTypeMessage.loadSetting, serverSetting); settingWindow?.webContents.send('languageSignal', serverSetting.general?.lang); - SendMessageToSettingWindow(SettingPageTypeMessage.selectMenu, { key: 'about'}); + SendMessageToSettingWindow(SettingPageTypeMessage.selectMenu, { key: 'about' }); }, 100) }) }) } - (async () => { +(async () => { await app.whenReady() onInitApplication(); })() @@ -291,40 +295,67 @@ ipcMain.on('setting-page', (event, arg) => { break; case SettingPageTypeMessage.showVersion: const currentVersion = app.getVersion(); - event.sender.send('setting-page', { type: SettingPageTypeMessage.showVersion, data: currentVersion}) + event.sender.send('setting-page', { type: SettingPageTypeMessage.showVersion, data: currentVersion }) break; case SettingPageTypeMessage.langChange: event.sender.send('languageSignal', arg.data); - eventEmitter.emit(EventLists.CHANGE_LANGUAGE, {code: arg.data}) + eventEmitter.emit(EventLists.CHANGE_LANGUAGE, { code: arg.data }) + break; + case SettingPageTypeMessage.updateSetting: + LocalStore.updateConfigSetting({ + general: { + autoUpdate: arg.data.autoUpdate, + updateCheckPeriode: arg.data.updateCheckPeriode + } + }) + createIntervalAutoUpdate() + event.sender.send('setting-page', { type: SettingPageTypeMessage.updateSettingResponse, data: true }) break; default: break; } }) +const createIntervalAutoUpdate = () => { + if (intervalUpdate) { + clearInterval(intervalUpdate) + } + const setting: WebServer = LocalStore.getStore('config'); + if (setting.general?.autoUpdate && setting.general.updateCheckPeriode) { + const checkIntervalSecond = parseInt(setting.general.updateCheckPeriode); + if (!Number.isNaN(checkIntervalSecond)) { + const intevalMS = checkIntervalSecond * 60 * 1000; + intervalUpdate = setInterval(() => { + updater.checkUpdateNotify(); + }, intevalMS) + } + } +} + + app.on('before-quit', async (e) => { - console.log('Before Quit'); + console.log('Before Quit'); - e.preventDefault(); + e.preventDefault(); - if (isServerRun) { - const exitConfirmationDialog = await dialog.showMessageBox({ + if (isServerRun) { + const exitConfirmationDialog = await dialog.showMessageBox({ message: '', - title: 'Warning', - detail: 'Server web still running, Are you sure to exit the app ?', + title: i18nextMainBackend.t('MESSAGE.WARNING'), + detail: i18nextMainBackend.t('MESSAGE.EXIT_MESSAGE'), buttons: [ - 'Yes', - 'No' + i18nextMainBackend.t('FORM.BUTTON.YES'), + i18nextMainBackend.t('FORM.BUTTON.NO') ] }) - if (exitConfirmationDialog.response === 0) { - // Stop the server from main - stopServer(); - } - } else { - app.exit(0); - } + if (exitConfirmationDialog.response === 0) { + // Stop the server from main + stopServer(); + } + } else { + app.exit(0); + } }); app.on('activate', () => { diff --git a/apps/server-web/src/main/updater.ts b/apps/server-web/src/main/updater.ts index 506764464..77539d78b 100644 --- a/apps/server-web/src/main/updater.ts +++ b/apps/server-web/src/main/updater.ts @@ -1,45 +1,69 @@ -import { autoUpdater, } from 'electron-updater'; +import { autoUpdater } from 'electron-updater'; import log from 'electron-log'; import EventEmitter from 'events'; import { EventLists } from './helpers/constant'; +import { InfoMessagesBox } from './windows/dialog'; +import i18n from 'i18next'; class AppUpdater { - constructor(eventEmitter: EventEmitter) { - log.transports.file.level = 'info'; - autoUpdater.logger = log; - autoUpdater.on('update-available', () => { - eventEmitter.emit(EventLists.UPDATE_AVAILABLE); - }) - - autoUpdater.on('error', (message) => { - eventEmitter.emit(EventLists.UPDATE_ERROR, message); - }) - - autoUpdater.on('update-not-available', (info) => { - eventEmitter.emit(EventLists.UPDATE_NOT_AVAILABLE, info); - }) - - autoUpdater.on('download-progress', (info) => { - eventEmitter.emit(EventLists.UPDATE_PROGRESS, info); - }) - - autoUpdater.on('update-downloaded', (data) => { - eventEmitter.emit(EventLists.UPDATE_DOWNLOADED, data); - }) - - autoUpdater.on('update-cancelled', (info) => { - eventEmitter.emit(EventLists.UPDATE_CANCELLED, info); - }) + constructor(eventEmitter: EventEmitter, i18nextMainBackend: typeof i18n) { + log.transports.file.level = 'info'; + autoUpdater.logger = log; + autoUpdater.on('update-available', (info) => { + eventEmitter.emit(EventLists.UPDATE_AVAILABLE, info); + if (!autoUpdater.autoDownload) { + InfoMessagesBox({ + title: '', + body: i18nextMainBackend.t('MESSAGE.UPDATE_AVAILABLE'), + btnLabel: { + ok: i18nextMainBackend.t('FORM.BUTTON.DOWNLOAD_NOW'), + close: i18nextMainBackend.t('FORM.BUTTON.LATER') + } + }).then((resDialog) => { + if (resDialog.response === 0) { + autoUpdater.downloadUpdate(); + } else { + eventEmitter.emit(EventLists.UPDATE_CANCELLED, info); + } + }) + } + }) + + autoUpdater.on('error', (message) => { + eventEmitter.emit(EventLists.UPDATE_ERROR, message); + }) + + autoUpdater.on('update-not-available', (info) => { + eventEmitter.emit(EventLists.UPDATE_NOT_AVAILABLE, info); + }) + + autoUpdater.on('download-progress', (info) => { + eventEmitter.emit(EventLists.UPDATE_PROGRESS, info); + }) + + autoUpdater.on('update-downloaded', (data) => { + eventEmitter.emit(EventLists.UPDATE_DOWNLOADED, data); + }) + + autoUpdater.on('update-cancelled', (info) => { + eventEmitter.emit(EventLists.UPDATE_CANCELLED, info); + }) // autoUpdater.checkForUpdatesAndNotify(); - } + } + + checkUpdate() { + autoUpdater.autoDownload = true; + autoUpdater.checkForUpdates(); + } - checkUpdate() { - autoUpdater.checkForUpdates(); - } + checkUpdateNotify() { + autoUpdater.autoDownload = false; + autoUpdater.checkForUpdates(); + } - installUpdate() { - autoUpdater.quitAndInstall(); - } + installUpdate() { + autoUpdater.quitAndInstall(); + } } export default AppUpdater; diff --git a/apps/server-web/src/main/util.ts b/apps/server-web/src/main/util.ts index 512e67035..32d5ec26a 100644 --- a/apps/server-web/src/main/util.ts +++ b/apps/server-web/src/main/util.ts @@ -1,10 +1,18 @@ /* eslint import/prefer-default-export: off */ import path from 'path'; +import url from 'url'; export function resolveHtmlPath(htmlFileName: string, hash: string) { if (process.env.NODE_ENV === 'development') { const port = process.env.PORT || 1212; return `http://localhost:${port}#/${hash}`; } - return `file://${path.resolve(__dirname, '../renderer/', `${htmlFileName}#/${hash}`)}`; + + const pathUrl = url.format({ + pathname: path.resolve(__dirname, '../renderer/', `${htmlFileName}`), + protocol: 'file:', + slashes: true, + hash: `/${hash}` + }) + return pathUrl; } diff --git a/apps/server-web/src/main/windows/dialog.ts b/apps/server-web/src/main/windows/dialog.ts new file mode 100644 index 000000000..49578a3ad --- /dev/null +++ b/apps/server-web/src/main/windows/dialog.ts @@ -0,0 +1,34 @@ +import { dialog } from 'electron'; + +interface InfoMessageBox { + title: string; + body: string; + action?: () => void; + actionClose?: () => void; + btnLabel: { + ok: string; + close: string; + } +} +export const InfoMessagesBox = async (options: InfoMessageBox) => { + return dialog.showMessageBox({ + message: options.body, + title: options.title, + type: 'info', + buttons: [options.btnLabel.ok, options.btnLabel.close] + }) +} + +interface ErrorMessageBox { + title: string; + body: string; + action: () => void; +} +export const ErrorMessagesBox = async (options: ErrorMessageBox) => { + await dialog.showMessageBox({ + message: options.body, + title: options.title, + type: 'error', + buttons: ['Ok'] + }) +} diff --git a/apps/server-web/src/renderer/App.css b/apps/server-web/src/renderer/App.css index f92d6b096..654b5a5e5 100644 --- a/apps/server-web/src/renderer/App.css +++ b/apps/server-web/src/renderer/App.css @@ -1,10 +1,141 @@ - @tailwind base; @tailwind components; @tailwind utilities; .dropdown:focus-within .dropdown-menu { - opacity:1; - transform: translate(0) scale(1); - visibility: visible; - } + opacity: 1; + transform: translate(0) scale(1); + visibility: visible; +} + +.select-trigger { + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 4px; + padding: 0 15px; + font-size: 13px; + line-height: 1; + height: 35px; + gap: 5px; + background-color: white; + color: var(--violet-11); + box-shadow: 0 2px 10px var(--black-a7); +} + +.select-trigger:hover { + background-color: var(--mauve-3); +} + +.select-trigger:focus { + box-shadow: 0 0 0 2px black; +} + +.select-trigger[data-placeholder] { + color: var(--violet-9); +} + +.select-icon { + color: Var(--violet-11); +} + +.select-content { + overflow: hidden; + background-color: white; + border-radius: 6px; + box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2); +} + +.select-viewport { + padding: 5px; +} + +.select-item { + font-size: 13px; + line-height: 1; + color: var(--violet-11); + border-radius: 3px; + display: flex; + align-items: center; + height: 25px; + padding: 0 35px 0 25px; + position: relative; + user-select: none; +} + +.select-item[data-disabled] { + color: var(--mauve-8); + pointer-events: none; +} + +.select-item[data-highlighted] { + outline: none; + background-color: var(--violet-9); + color: var(--violet-1); +} + +.select-label { + padding: 0 25px; + font-size: 12px; + line-height: 25px; + color: var(--mauve-11); +} + +.select-separator { + height: 1px; + background-color: var(--violet-6); + margin: 5px; +} + +.select-item-indicator { + position: absolute; + left: 0; + width: 25px; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.select-scroll-button { + display: flex; + align-items: center; + justify-content: center; + height: 25px; + background-color: white; + color: var(--violet-11); + cursor: default; +} + +.switch-root { + width: 42px; + height: 25px; + background-color: rgb(126 126 143/var(--tw-bg-opacity)); + border-radius: 9999px; + position: relative; + box-shadow: 0 2px 10px rgb(96 165 250/var(--tw-bg-opacity)); + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +.switch-root:focus { + box-shadow: 0 0 0 2px black; +} + +.switch-root[data-state='checked'] { + background-color: rgb(96 165 250/var(--tw-bg-opacity)); +} + +.switch-thumb { + display: block; + width: 21px; + height: 21px; + background-color: white; + border-radius: 9999px; + box-shadow: 0 2px 2px var(--black-a7); + transition: transform 100ms; + transform: translateX(2px); + will-change: transform; +} + +.switch-thumb[data-state='checked'] { + transform: translateX(19px); +} diff --git a/apps/server-web/src/renderer/components/About.tsx b/apps/server-web/src/renderer/components/About.tsx index 3e07af722..be7030117 100644 --- a/apps/server-web/src/renderer/components/About.tsx +++ b/apps/server-web/src/renderer/components/About.tsx @@ -4,7 +4,7 @@ type Props = { }; export const AboutComponent = (props: Props) => { return ( -
+
diff --git a/apps/server-web/src/renderer/components/Select.tsx b/apps/server-web/src/renderer/components/Select.tsx new file mode 100644 index 000000000..c5feb7a0d --- /dev/null +++ b/apps/server-web/src/renderer/components/Select.tsx @@ -0,0 +1,85 @@ +import * as Select from '@radix-ui/react-select'; +import { + CheckIcon, + ChevronDownIcon, + ChevronUpIcon, +} from '@radix-ui/react-icons'; +import { useTranslation } from 'react-i18next'; +import { forwardRef } from 'react'; +import classnames from 'classnames'; +type SelectItems = { + label: string; + value: string; +}; +type Props = { + title: string; + items: SelectItems[]; + defaultValue: string; + value: string; + disabled: boolean; + onValueChange: (val: string) => void; +}; + +const SelectItem = forwardRef( + ({ children, className, ...props }: any, forwardedRef) => { + return ( + + {children} + + + + + ); + }, +); +export const SelectComponent = ({ + title, + items, + defaultValue, + value, + disabled, + onValueChange, +}: Props) => { + const { t } = useTranslation(); + return ( + + + + + + + + + + + + + + + {items.map((item) => ( + + {t(`FORM.LABELS.UPDATE_OPTIONS.${item.label}`)} + + ))} + + + + + + + + + ); +}; diff --git a/apps/server-web/src/renderer/components/Server.tsx b/apps/server-web/src/renderer/components/Server.tsx index 3550ca3ab..f72589144 100644 --- a/apps/server-web/src/renderer/components/Server.tsx +++ b/apps/server-web/src/renderer/components/Server.tsx @@ -30,75 +30,86 @@ export const ServerComponent = (props: Props) => { }; return ( <> -
+
-
-
-
-
- +
+
+
+
+
+ +
-
- +
+
+ +
+
+ +
-
-
-
- +
+
+ +
+
+ +
-
- -
-
-
-
- -
-
- +
+
+ +
+
+ +
- + +
+ +
+
+
{children}
diff --git a/apps/server-web/src/renderer/components/Toast.tsx b/apps/server-web/src/renderer/components/Toast.tsx new file mode 100644 index 000000000..e63287440 --- /dev/null +++ b/apps/server-web/src/renderer/components/Toast.tsx @@ -0,0 +1,59 @@ +import * as React from 'react'; +import * as Toast from '@radix-ui/react-toast'; +type Props = { + title: string; + message: string; + show: boolean; + autoClose: boolean; + timeout: number; + onClose: () => void; +}; +export const ToastComponent = ({ + title, + message, + show, + autoClose, + timeout, + onClose, +}: Props) => { + const timerRef = React.useRef(0); + React.useEffect(() => { + // return () => clearTimeout(timerRef.current); + if (autoClose) { + clearTimeout(timerRef.current); + timerRef.current = window.setTimeout(() => { + onClose(); + }, timeout); + } + }, []); + + return ( + + + + {title} + + + {message} + + + + + + + + ); +}; diff --git a/apps/server-web/src/renderer/components/Updater.tsx b/apps/server-web/src/renderer/components/Updater.tsx index 322758312..5b6c2dd16 100644 --- a/apps/server-web/src/renderer/components/Updater.tsx +++ b/apps/server-web/src/renderer/components/Updater.tsx @@ -1,27 +1,39 @@ -import { EverTeamsLogo } from './svgs'; import { useTranslation } from 'react-i18next'; +import * as Switch from '@radix-ui/react-switch'; +import { SelectComponent } from './Select'; +import { useEffect, useState } from 'react'; +import { ToastComponent } from './Toast'; +import { SettingPageTypeMessage } from '../../main/helpers/constant'; interface UpdaterStates { state: - | 'check-update' - | 'update-available' - | 'downloading' - | 'downloaded' - | 'error' - | 'not-started' - | 'up-to-date'; + | 'check-update' + | 'update-available' + | 'downloading' + | 'downloaded' + | 'error' + | 'not-started' + | 'up-to-date' + | 'cancel' + ; data: any; label: - | 'CHECKING' - | 'DOWNLOADING' - | 'QUIT_N_INSTALL' - | 'UP_TO_DATE' - | 'UPDATE_AVAILABLE' - | 'CHECK_FOR_UPDATE'; + | 'CHECKING' + | 'DOWNLOADING' + | 'QUIT_N_INSTALL' + | 'UP_TO_DATE' + | 'UPDATE_AVAILABLE' + | 'CHECK_FOR_UPDATE'; } type PropsProgress = { updateStates: UpdaterStates; }; + +type UpdateSetting = { + autoUpdate: boolean; + updateCheckPeriode: string; +}; + const ProgressComponent = (props: PropsProgress) => { const { t } = useTranslation(); return ( @@ -63,30 +75,165 @@ type Props = { loading: boolean; updateStates: UpdaterStates; Popup: JSX.Element; + data: UpdateSetting; + changeAutoUpdate: (data: UpdateSetting) => void; + saveSettingUpdate: (data: UpdateSetting) => void; +}; + +type RangeUpdates = { + value: string; + label: string; }; export const UpdaterComponent = (props: Props) => { const { t } = useTranslation(); + const [rangeUpdate, setRangeUpdate] = useState([ + { + value: '30', + label: `30_MINUTES`, + }, + { + value: '60', + label: `A_HOURS`, + }, + { + value: '180', + label: `3_HOURS`, + }, + { + value: '1140', + label: `A_DAY`, + }, + ]); + + const [toastShow, setToastShow] = useState(false); + + const setOpen = () => { + setToastShow(false); + setTimeout(() => { + setToastShow(true); + }, 100); + }; + + const CloseToast = () => { + setToastShow(false); + }; + + const onSelectPeriode = (value: string) => { + props.changeAutoUpdate({ + autoUpdate: props.data.autoUpdate, + updateCheckPeriode: value, + }); + props.saveSettingUpdate({ + autoUpdate: props.data.autoUpdate, + updateCheckPeriode: value, + }); + }; + + useEffect(() => { + window.electron.ipcRenderer.once('setting-page', (arg: any) => { + switch (arg.type) { + case SettingPageTypeMessage.updateSettingResponse: + setOpen(); + break; + + default: + break; + } + }); + }, []); + return ( <> -
-
-
- +
+
+ {t('MENU.UPDATER')} +
+
+ + {t('FORM.LABELS.AUTO_UPDATE_TITLE')} + +
+
+ + {t('FORM.LABELS.AUTO_UPDATE_SUBTITLE')} + +
+
+ +
+
+ { + setOpen(); + props.changeAutoUpdate({ + autoUpdate: value, + updateCheckPeriode: props.data.updateCheckPeriode, + }); + props.saveSettingUpdate({ + autoUpdate: value, + updateCheckPeriode: props.data.updateCheckPeriode, + }); + }} + checked={props.data.autoUpdate} + > + + + +
+
+ +
+
+
+ +
+
+
+ + {t('FORM.LABELS.CHECK_UPDATE_TITLE')} + +
+
+ + {t('FORM.LABELS.CHECK_UPDATE_SUBTITLE')} +
-

-
+
{props.Popup} + ); }; diff --git a/apps/server-web/src/renderer/pages/Setting.tsx b/apps/server-web/src/renderer/pages/Setting.tsx index becabebb5..14440074f 100644 --- a/apps/server-web/src/renderer/pages/Setting.tsx +++ b/apps/server-web/src/renderer/pages/Setting.tsx @@ -17,21 +17,23 @@ interface SideMenu { interface UpdaterStates { state: - | 'check-update' - | 'update-available' - | 'downloading' - | 'downloaded' - | 'error' - | 'not-started' - | 'up-to-date'; + | 'check-update' + | 'update-available' + | 'downloading' + | 'downloaded' + | 'error' + | 'not-started' + | 'up-to-date' + | 'cancel' + ; data: any; label: - | 'CHECKING' - | 'DOWNLOADING' - | 'QUIT_N_INSTALL' - | 'UP_TO_DATE' - | 'UPDATE_AVAILABLE' - | 'CHECK_FOR_UPDATE'; + | 'CHECKING' + | 'DOWNLOADING' + | 'QUIT_N_INSTALL' + | 'UP_TO_DATE' + | 'UPDATE_AVAILABLE' + | 'CHECK_FOR_UPDATE'; } interface IServerSetting { @@ -50,11 +52,16 @@ interface Languages { label: string; } +type UpdateSetting = { + autoUpdate: boolean; + updateCheckPeriode: string; +}; + export function Setting() { const [menus, setMenu] = useState([ { - displayName: 'GENERAL', - key: 'general', + displayName: 'UPDATER', + key: 'updater', isActive: true, }, { @@ -62,11 +69,6 @@ export function Setting() { key: 'server', isActive: false, }, - { - displayName: 'UPDATER', - key: 'updater', - isActive: false, - }, { displayName: 'ABOUT', key: 'about', @@ -74,6 +76,11 @@ export function Setting() { }, ]); + const [updateSetting, setUpdateSetting] = useState({ + autoUpdate: false, + updateCheckPeriode: '180', + }); + const [langs, setLangs] = useState([ { code: 'en', @@ -121,11 +128,14 @@ export function Setting() { }; const changeLanguage = (lang: Languages) => { - console.log(lang); sendingMessageToMain(lang.code, SettingPageTypeMessage.langChange); setLng(lang.code); }; + const saveSettingUpdate = (data: UpdateSetting) => { + sendingMessageToMain(data, SettingPageTypeMessage.updateSetting); + }; + const sendingMessageToMain = (data: any, type: string) => { window.electron.ipcRenderer.sendMessage('setting-page', { type, @@ -133,6 +143,10 @@ export function Setting() { }); }; + const updateDataSettingUpdate = (data: UpdateSetting) => { + setUpdateSetting(data); + }; + const [serverSetting, setServerSetting] = useState({ PORT: 3002, GAUZY_API_SERVER_URL: '', @@ -190,6 +204,9 @@ export function Setting() { checkForUpdate={checkForUpdate} loading={loading} updateStates={updateStates} + changeAutoUpdate={updateDataSettingUpdate} + data={updateSetting} + saveSettingUpdate={saveSettingUpdate} Popup={ + ); diff --git a/apps/server-web/tailwind.config.js b/apps/server-web/tailwind.config.js index 1d946f0d4..95d843bfb 100644 --- a/apps/server-web/tailwind.config.js +++ b/apps/server-web/tailwind.config.js @@ -118,10 +118,27 @@ module.exports = { from: { height: 'var(--radix-accordion-content-height)' }, to: { height: 0 }, }, + hide: { + from: { opacity: '1' }, + to: { opacity: '0' }, + }, + slideIn: { + from: { + transform: 'translateX(calc(100% + var(--viewport-padding)))', + }, + to: { transform: 'translateX(0)' }, + }, + swipeOut: { + from: { transform: 'translateX(var(--radix-toast-swipe-end-x))' }, + to: { transform: 'translateX(calc(100% + var(--viewport-padding)))' }, + }, }, animation: { 'accordion-down': 'accordion-down 0.2s ease-out', 'accordion-up': 'accordion-up 0.2s ease-out', + hide: 'hide 100ms ease-in', + slideIn: 'slideIn 150ms cubic-bezier(0.16, 1, 0.3, 1)', + swipeOut: 'swipeOut 100ms ease-out', }, typography: { DEFAULT: { diff --git a/yarn.lock b/yarn.lock index aefbd5cb1..6c6e56058 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6080,6 +6080,19 @@ dependencies: "@radix-ui/react-compose-refs" "1.1.0" +"@radix-ui/react-switch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-switch/-/react-switch-1.1.0.tgz#fcf8e778500f1d60d4b2bec2fc3fad77a7c118e3" + integrity sha512-OBzy5WAj641k0AOSpKQtreDMe+isX0MQJ1IVyF03ucdF3DunOnROVrjWs8zsXUxC3zfZ6JL9HFVCUlMghz9dJw== + dependencies: + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-use-previous" "1.1.0" + "@radix-ui/react-use-size" "1.1.0" + "@radix-ui/react-toast@^1.1.4": version "1.1.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.1.4.tgz#9a7fc2d71700886f3292f7699c905f1e01be59e1" @@ -10223,6 +10236,11 @@ class-variance-authority@^0.7.0: dependencies: clsx "2.0.0" +classnames@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== + clean-css@^5.2.2: version "5.3.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd"