From 2e26126a4a61a8689c306fd7d75faa7b5e86fc3d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:16:46 +0000 Subject: [PATCH 01/14] chore(deps): bump ws from 6.2.2 to 6.2.3 in /apps/mobile Bumps [ws](https://github.com/websockets/ws) from 6.2.2 to 6.2.3. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/6.2.2...6.2.3) --- updated-dependencies: - dependency-name: ws dependency-type: indirect ... Signed-off-by: dependabot[bot] --- apps/mobile/yarn.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/mobile/yarn.lock b/apps/mobile/yarn.lock index 40b336448..59df746a7 100644 --- a/apps/mobile/yarn.lock +++ b/apps/mobile/yarn.lock @@ -15290,21 +15290,21 @@ write-file-atomic@^4.0.2: signal-exit "^3.0.7" ws@^6.2.2: - version "6.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" - integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== + version "6.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.3.tgz#ccc96e4add5fd6fedbc491903075c85c5a11d9ee" + integrity sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA== dependencies: async-limiter "~1.0.0" ws@^7, ws@^7.0.0, ws@^7.5.1: - version "7.5.9" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== ws@^8.11.0, ws@^8.12.1, ws@^8.13.0: - version "8.14.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" - integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== + version "8.17.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== xcode@3.0.1, xcode@^3.0.0, xcode@^3.0.1: version "3.0.1" From 670d5bf165da824d2fda8e2f0e6cd5143b936530 Mon Sep 17 00:00:00 2001 From: sutralia Date: Wed, 19 Jun 2024 21:52:58 +0700 Subject: [PATCH 02/14] fix: styling code and anable try fucntion --- apps/server-web/src/main/helpers/constant.ts | 4 +- apps/server-web/src/main/main.ts | 12 +++ apps/server-web/src/main/tray.ts | 2 +- .../src/renderer/components/Popup.tsx | 12 +-- .../src/renderer/components/Server.tsx | 3 +- .../src/renderer/components/SideBar.tsx | 73 +++++++++---------- .../src/renderer/components/Updater.tsx | 2 +- .../components/svgs/EverTeamsLogo.tsx | 10 +-- .../server-web/src/renderer/pages/Setting.tsx | 3 + 9 files changed, 68 insertions(+), 53 deletions(-) diff --git a/apps/server-web/src/main/helpers/constant.ts b/apps/server-web/src/main/helpers/constant.ts index 33e57c80e..8924b99fb 100644 --- a/apps/server-web/src/main/helpers/constant.ts +++ b/apps/server-web/src/main/helpers/constant.ts @@ -4,6 +4,7 @@ export const EventLists = { 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', @@ -23,5 +24,6 @@ export const EventLists = { updateError: 'update-error', upToDate: 'uptodate', mainResponse: 'main-response', - showVersion: 'show-version' + showVersion: 'show-version', + selectMenu: 'select-menu' } diff --git a/apps/server-web/src/main/main.ts b/apps/server-web/src/main/main.ts index d1730bba7..a2bba8e40 100644 --- a/apps/server-web/src/main/main.ts +++ b/apps/server-web/src/main/main.ts @@ -217,6 +217,18 @@ const onInitApplication = () => { eventEmitter.on(EventLists.UPDATE_CANCELLED, (data)=> { console.log('UPDATE_CANCELLED', data); }) + + eventEmitter.on(EventLists.gotoAbout, async () => { + if (!settingWindow) { + await createWindow(); + } + const serverSetting = LocalStore.getStore('config'); + settingWindow?.show(); + settingWindow?.webContents.once('did-finish-load', () => { + SendMessageToSettingWindow(SettingPageTypeMessage.loadSetting, serverSetting); + SendMessageToSettingWindow(SettingPageTypeMessage.selectMenu, { key: 'about'}) + }) + }) } (async () => { diff --git a/apps/server-web/src/main/tray.ts b/apps/server-web/src/main/tray.ts index b5d0d54c3..7e1baed1d 100644 --- a/apps/server-web/src/main/tray.ts +++ b/apps/server-web/src/main/tray.ts @@ -44,7 +44,7 @@ export const defaultTrayMenuItem = (eventEmitter: EventEmitter) => { id: 'APP_ABOUT', label: 'About Gauzy Web Server', async click() { - console.log('about') + eventEmitter.emit(EventLists.gotoAbout) } }, { diff --git a/apps/server-web/src/renderer/components/Popup.tsx b/apps/server-web/src/renderer/components/Popup.tsx index 95c2b880b..441371db8 100644 --- a/apps/server-web/src/renderer/components/Popup.tsx +++ b/apps/server-web/src/renderer/components/Popup.tsx @@ -35,13 +35,13 @@ export function Popup(props: Props) { xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" - stroke-width="1.5" + strokeWidth="1.5" stroke="currentColor" className="h-8 w-8 text-white" > @@ -60,9 +60,9 @@ export function Popup(props: Props) { aria-hidden="true" > diff --git a/apps/server-web/src/renderer/components/Server.tsx b/apps/server-web/src/renderer/components/Server.tsx index 98cffea16..2cedb0921 100644 --- a/apps/server-web/src/renderer/components/Server.tsx +++ b/apps/server-web/src/renderer/components/Server.tsx @@ -1,5 +1,4 @@ -import { useEffect, useState } from 'react'; -import { SettingPageTypeMessage } from '../../main/helpers/constant'; +import { useState } from 'react'; interface IServerSetting { PORT: number; diff --git a/apps/server-web/src/renderer/components/SideBar.tsx b/apps/server-web/src/renderer/components/SideBar.tsx index 6b96fc19e..5fa963e52 100644 --- a/apps/server-web/src/renderer/components/SideBar.tsx +++ b/apps/server-web/src/renderer/components/SideBar.tsx @@ -1,42 +1,41 @@ import { Link } from 'react-router-dom'; interface SideMenu { - displayName: string; - key: string; - isActive: boolean; - } -type Props = { - children: string | JSX.Element | JSX.Element[], - menus: SideMenu[], - menuChange: (key: string) => void + displayName: string; + key: string; + isActive: boolean; } -export function SideBar({ children, menus, menuChange }:Props) { - return ( -
-
-
-
    - {menus.length && menus.map((menu) => ( -
  • - { - menuChange(menu.key); - }} - > - - {/* */} - - {menu.displayName} - -
  • - ))} -
-
-
-
- {children} -
+type Props = { + children: string | JSX.Element | JSX.Element[]; + menus: SideMenu[]; + menuChange: (key: string) => void; +}; +export function SideBar({ children, menus, menuChange }: Props) { + return ( +
+
+
+
    + {menus.length > 0 && + menus.map((menu) => ( +
  • + { + menuChange(menu.key); + }} + > + + + {menu.displayName} + + +
  • + ))} +
- ) +
+
{children}
+
+ ); } diff --git a/apps/server-web/src/renderer/components/Updater.tsx b/apps/server-web/src/renderer/components/Updater.tsx index 48588eccb..2536015b5 100644 --- a/apps/server-web/src/renderer/components/Updater.tsx +++ b/apps/server-web/src/renderer/components/Updater.tsx @@ -38,7 +38,7 @@ const ProgressComponent = (props: PropsProgress) => { cy="12" r="10" stroke="currentColor" - stroke-width="4" + strokeWidth="4" > { teams diff --git a/apps/server-web/src/renderer/pages/Setting.tsx b/apps/server-web/src/renderer/pages/Setting.tsx index a4490c9dd..f39ac2e52 100644 --- a/apps/server-web/src/renderer/pages/Setting.tsx +++ b/apps/server-web/src/renderer/pages/Setting.tsx @@ -243,6 +243,9 @@ export function Setting() { case SettingPageTypeMessage.showVersion: setVersion(arg.data); break; + case SettingPageTypeMessage.selectMenu: + menuChange(arg.data.key); + break; default: break; } From 72be7a2b17413cdfdd310e51eb1575cc759ca091 Mon Sep 17 00:00:00 2001 From: sutralia Date: Wed, 19 Jun 2024 22:04:25 +0700 Subject: [PATCH 03/14] fix: miss spelling constant value --- apps/server-web/src/main/helpers/constant.ts | 2 +- apps/server-web/src/renderer/components/Updater.tsx | 6 +++--- apps/server-web/src/renderer/pages/Setting.tsx | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/server-web/src/main/helpers/constant.ts b/apps/server-web/src/main/helpers/constant.ts index 8924b99fb..1a90e6ae5 100644 --- a/apps/server-web/src/main/helpers/constant.ts +++ b/apps/server-web/src/main/helpers/constant.ts @@ -22,7 +22,7 @@ export const EventLists = { installUpdate: 'install-update', saveSetting: 'save-setting', updateError: 'update-error', - upToDate: 'uptodate', + upToDate: 'up-to-date', mainResponse: 'main-response', showVersion: 'show-version', selectMenu: 'select-menu' diff --git a/apps/server-web/src/renderer/components/Updater.tsx b/apps/server-web/src/renderer/components/Updater.tsx index 2536015b5..9023e9ce2 100644 --- a/apps/server-web/src/renderer/components/Updater.tsx +++ b/apps/server-web/src/renderer/components/Updater.tsx @@ -8,16 +8,16 @@ interface UpdaterStates { | 'downloaded' | 'error' | 'not-started' - | 'uptodate'; + | 'up-to-date'; data: any; label: | 'Checking' | 'Downloading' | 'Quit and Install' - | 'Uptodate' + | 'Up to date' | 'Update Available' | 'Check For Update' - | 'Uptodate'; + | 'Up to date'; } type PropsProgress = { updateStates: UpdaterStates; diff --git a/apps/server-web/src/renderer/pages/Setting.tsx b/apps/server-web/src/renderer/pages/Setting.tsx index f39ac2e52..70dd58ae6 100644 --- a/apps/server-web/src/renderer/pages/Setting.tsx +++ b/apps/server-web/src/renderer/pages/Setting.tsx @@ -22,16 +22,16 @@ interface UpdaterStates { | 'downloaded' | 'error' | 'not-started' - | 'uptodate'; + | 'up-to-date'; data: any; label: | 'Checking' | 'Downloading' | 'Quit and Install' - | 'Uptodate' + | 'Up to date' | 'Update Available' | 'Check For Update' - | 'Uptodate'; + | 'Up to date'; } interface IServerSetting { @@ -219,9 +219,9 @@ export function Setting() { break; case SettingPageTypeMessage.upToDate: setUpdateState({ - state: 'uptodate', + state: 'up-to-date', data: null, - label: 'Uptodate', + label: 'Up to date', }); setLoading(false); break; From 58d5b7aa68fd2acfebf8d26b925ae8cd4c8cab51 Mon Sep 17 00:00:00 2001 From: "Gloire Mutaliko (Salva)" <86450367+GloireMutaliko21@users.noreply.github.com> Date: Fri, 21 Jun 2024 17:52:24 +0200 Subject: [PATCH 04/14] Filter on Daily Plan Tab (#2647) * feat: load future and today plans on filter * feat: filter daily plans by date * fix: improve some daily plan code structure * fix: colors and backgrounds --- apps/web/app/hooks/features/useDailyPlan.ts | 119 +++++++++++++-- apps/web/app/stores/daily-plan.ts | 5 + .../task/daily-plan/daily-plan-filter.tsx | 136 ++++++++++++++++++ .../features/task/daily-plan/future-tasks.tsx | 16 +-- .../features/task/daily-plan/outstanding.tsx | 29 +--- .../features/task/daily-plan/past-tasks.tsx | 16 +-- apps/web/lib/features/task/task-filters.tsx | 21 ++- apps/web/lib/features/user-profile-plans.tsx | 47 +++--- apps/web/styles/globals.css | 28 ++++ 9 files changed, 335 insertions(+), 82 deletions(-) create mode 100644 apps/web/lib/features/task/daily-plan/daily-plan-filter.tsx diff --git a/apps/web/app/hooks/features/useDailyPlan.ts b/apps/web/app/hooks/features/useDailyPlan.ts index 4736e8514..d60318923 100644 --- a/apps/web/app/hooks/features/useDailyPlan.ts +++ b/apps/web/app/hooks/features/useDailyPlan.ts @@ -6,6 +6,7 @@ import { useQuery } from '../useQuery'; import { dailyPlanFetchingState, dailyPlanListState, + employeePlansListState, myDailyPlanListState, profileDailyPlanListState, taskPlans, @@ -42,6 +43,7 @@ export function useDailyPlan() { const [dailyPlan, setDailyPlan] = useRecoilState(dailyPlanListState); const [myDailyPlans, setMyDailyPlans] = useRecoilState(myDailyPlanListState); const [profileDailyPlans, setProfileDailyPlans] = useRecoilState(profileDailyPlanListState); + const [employeePlans, setEmployeePlans] = useRecoilState(employeePlansListState); const [taskPlanList, setTaskPlans] = useRecoilState(taskPlans); const [dailyPlanFetching, setDailyPlanFetching] = useRecoilState(dailyPlanFetchingState); const { firstLoadData: firstLoadDailyPlanData, firstLoad } = useFirstLoad(); @@ -75,9 +77,10 @@ export function useDailyPlan() { queryCall(employeeId).then((response) => { const { items, total } = response.data; setProfileDailyPlans({ items, total }); + setEmployeePlans(items); }); }, - [queryCall, setProfileDailyPlans] + [queryCall, setEmployeePlans, setProfileDailyPlans] ); const getPlansByTask = useCallback( @@ -97,68 +100,162 @@ export function useDailyPlan() { total: profileDailyPlans.total + 1, items: [...profileDailyPlans.items, res.data] }); + setEmployeePlans([...employeePlans, res.data]); getMyDailyPlans(); return res; } }, - [createQueryCall, getMyDailyPlans, profileDailyPlans, setProfileDailyPlans, user?.tenantId] + [ + createQueryCall, + employeePlans, + getMyDailyPlans, + profileDailyPlans, + setEmployeePlans, + setProfileDailyPlans, + user?.tenantId + ] ); const updateDailyPlan = useCallback( async (data: IUpdateDailyPlan, planId: string) => { const res = await updateQueryCall(data, planId); const updated = profileDailyPlans.items.filter((plan) => plan.id != planId); + const updatedEmployee = employeePlans.filter((plan) => plan.id != planId); setProfileDailyPlans({ total: profileDailyPlans.total, items: [...updated, res.data] }); + setEmployeePlans([...updatedEmployee, res.data]); return res; }, - [profileDailyPlans.items, profileDailyPlans.total, setProfileDailyPlans, updateQueryCall] + [employeePlans, profileDailyPlans, setEmployeePlans, setProfileDailyPlans, updateQueryCall] ); const addTaskToPlan = useCallback( async (data: IDailyPlanTasksUpdate, planId: string) => { const res = await addTaskToPlanQueryCall(data, planId); const updated = profileDailyPlans.items.filter((plan) => plan.id != planId); + const updatedEmployee = employeePlans.filter((plan) => plan.id != planId); setProfileDailyPlans({ total: profileDailyPlans.total, items: [...updated, res.data] }); + setEmployeePlans([...updatedEmployee, res.data]); getMyDailyPlans(); return res; }, - [addTaskToPlanQueryCall, getMyDailyPlans, profileDailyPlans, setProfileDailyPlans] + [ + addTaskToPlanQueryCall, + employeePlans, + getMyDailyPlans, + profileDailyPlans, + setEmployeePlans, + setProfileDailyPlans + ] ); const removeTaskFromPlan = useCallback( async (data: IDailyPlanTasksUpdate, planId: string) => { const res = await removeTAskFromPlanQueryCall(data, planId); const updated = profileDailyPlans.items.filter((plan) => plan.id != planId); + const updatedEmployee = employeePlans.filter((plan) => plan.id != planId); setProfileDailyPlans({ total: profileDailyPlans.total, items: [...updated, res.data] }); + setEmployeePlans([...updatedEmployee, res.data]); getMyDailyPlans(); return res; }, - [getMyDailyPlans, profileDailyPlans, removeTAskFromPlanQueryCall, setProfileDailyPlans] + [ + employeePlans, + getMyDailyPlans, + profileDailyPlans, + removeTAskFromPlanQueryCall, + setEmployeePlans, + setProfileDailyPlans + ] ); const deleteDailyPlan = useCallback( async (planId: string) => { const res = await deleteDailyPlanQueryCall(planId); const updated = profileDailyPlans.items.filter((plan) => plan.id != planId); + const updatedEmployee = employeePlans.filter((plan) => plan.id != planId); setProfileDailyPlans({ total: updated.length, items: [...updated] }); + setEmployeePlans([...updatedEmployee]); getMyDailyPlans(); return res; }, - [deleteDailyPlanQueryCall, getMyDailyPlans, profileDailyPlans.items, setProfileDailyPlans] + [ + deleteDailyPlanQueryCall, + employeePlans, + getMyDailyPlans, + profileDailyPlans.items, + setEmployeePlans, + setProfileDailyPlans + ] ); + const ascSortedPlans = + profileDailyPlans.items && + [...profileDailyPlans.items].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); + const futurePlans = ascSortedPlans?.filter((plan) => { + const planDate = new Date(plan.date); + const today = new Date(); + today.setHours(23, 59, 59, 0); // Set today time to exclude timestamps in comparization + return planDate.getTime() >= today.getTime(); + }); + + const descSortedPlans = + profileDailyPlans.items && + [...profileDailyPlans.items].sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + const pastPlans = descSortedPlans?.filter((plan) => { + const planDate = new Date(plan.date); + const today = new Date(); + today.setHours(0, 0, 0, 0); // Set today time to exclude timestamps in comparization + return planDate.getTime() < today.getTime(); + }); + + const outstandingPlans = + profileDailyPlans.items && + [...profileDailyPlans.items] + // Exclude today plans + .filter((plan) => !plan.date?.toString()?.startsWith(new Date()?.toISOString().split('T')[0])) + + // Exclude future plans + .filter((plan) => { + const planDate = new Date(plan.date); + const today = new Date(); + today.setHours(23, 59, 59, 0); // Set today time to exclude timestamps in comparization + return planDate.getTime() <= today.getTime(); + }) + .sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()) + .map((plan) => ({ + ...plan, + // Include only no completed tasks + tasks: plan.tasks?.filter((task) => task.status !== 'completed') + })) + .filter((plan) => plan.tasks?.length && plan.tasks.length > 0); + + const todayPlan = + profileDailyPlans.items && + [...profileDailyPlans.items].filter((plan) => + plan.date?.toString()?.startsWith(new Date()?.toISOString().split('T')[0]) + ); + + const sortedPlans = + profileDailyPlans.items && + [...profileDailyPlans.items].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); + useEffect(() => { getMyDailyPlans(); }, [getMyDailyPlans]); return { dailyPlan, - profileDailyPlans, setDailyPlan, + + profileDailyPlans, + setProfileDailyPlans, dailyPlanFetching, + employeePlans, + setEmployeePlans, + taskPlanList, firstLoadDailyPlanData, @@ -189,6 +286,12 @@ export function useDailyPlan() { removeTaskFromPlanLoading, deleteDailyPlan, - deleteDailyPlanLoading + deleteDailyPlanLoading, + + futurePlans, + pastPlans, + outstandingPlans, + todayPlan, + sortedPlans }; } diff --git a/apps/web/app/stores/daily-plan.ts b/apps/web/app/stores/daily-plan.ts index 1e75cd1a6..ab4b53e8b 100644 --- a/apps/web/app/stores/daily-plan.ts +++ b/apps/web/app/stores/daily-plan.ts @@ -16,6 +16,11 @@ export const profileDailyPlanListState = atom>({ default: { items: [], total: 0 } }); +export const employeePlansListState = atom({ + key: 'employeePlansListState', + default: [] +}); + export const taskPlans = atom({ key: 'taskPlansList', default: [] diff --git a/apps/web/lib/features/task/daily-plan/daily-plan-filter.tsx b/apps/web/lib/features/task/daily-plan/daily-plan-filter.tsx new file mode 100644 index 000000000..434178523 --- /dev/null +++ b/apps/web/lib/features/task/daily-plan/daily-plan-filter.tsx @@ -0,0 +1,136 @@ +import { formatDayPlanDate } from '@app/helpers'; +import { useDailyPlan } from '@app/hooks'; +import { clsxm } from '@app/utils'; +import { Listbox, Transition } from '@headlessui/react'; +import { ChevronDownIcon } from '@heroicons/react/20/solid'; +import { CircleIcon } from 'assets/svg'; +import { Card, Tooltip } from 'lib/components'; +import { Fragment, PropsWithChildren, useEffect, useState } from 'react'; + +export function DailyPlanDropDownItem({ + children, + label, + active = true, + checked = false, + showIcon = true, + icon +}: PropsWithChildren<{ + label: string; + active?: boolean; + checked?: boolean; + showIcon?: boolean; + icon?: React.ReactNode; +}>) { + return ( +
+
+ {checked ? ( + + + + ) : ( + <>{showIcon && active && icon} + )} +
{label}
+
+ {children} +
+ ); +} + +export function DailyPlanFilter({ employeeId }: { employeeId: string }) { + const [selectedPlans, setSelectedPlans] = useState([]); + const { employeePlans, getEmployeeDayPlans, setProfileDailyPlans } = useDailyPlan(); + const filteredPlans = employeePlans; + + useEffect(() => { + if (selectedPlans.length === 0) { + getEmployeeDayPlans(employeeId); + } + }, [selectedPlans, getEmployeeDayPlans, employeeId]); + + useEffect(() => { + setProfileDailyPlans((prevState) => { + const filtered = employeePlans.filter((plan) => + selectedPlans.length > 0 ? selectedPlans.includes(plan.date.toString()) : true + ); + + // Only update if the items have changed + if (JSON.stringify(filtered) !== JSON.stringify(employeePlans)) { + return { ...prevState, items: filtered }; + } + return prevState; + }); + }, [employeePlans, selectedPlans, setProfileDailyPlans]); + + return ( + +
+ + {({ open }) => { + return ( + <> + + 0 ? `Items(${selectedPlans.length})` : 'Plans'} + icon={ + + + + } + active={true} + > + + + + + + + + {filteredPlans.map((item) => ( + +
  • + +
  • +
    + ))} +
    +
    +
    + + ); + }} +
    +
    +
    + ); +} diff --git a/apps/web/lib/features/task/daily-plan/future-tasks.tsx b/apps/web/lib/features/task/daily-plan/future-tasks.tsx index eda300ea7..303aeedbe 100644 --- a/apps/web/lib/features/task/daily-plan/future-tasks.tsx +++ b/apps/web/lib/features/task/daily-plan/future-tasks.tsx @@ -1,5 +1,4 @@ import { formatDayPlanDate, tomorrowDate } from '@app/helpers'; -import { IDailyPlan } from '@app/interfaces'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@components/ui/accordion'; import { EmptyPlans, PlanHeader } from 'lib/features/user-profile-plans'; import { TaskCard } from '../task-card'; @@ -7,26 +6,19 @@ import { Button } from '@components/ui/button'; import { useCanSeeActivityScreen, useDailyPlan } from '@app/hooks'; import { ReloadIcon } from '@radix-ui/react-icons'; -export function FutureTasks({ dayPlans, profile }: { dayPlans: IDailyPlan[]; profile: any }) { - const ascSortedPlans = [...dayPlans].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); - const filteredPlans = ascSortedPlans.filter((plan) => { - const planDate = new Date(plan.date); - const today = new Date(); - today.setHours(23, 59, 59, 0); // Set today time to exclude timestamps in comparization - return planDate.getTime() >= today.getTime(); - }); - const { deleteDailyPlan, deleteDailyPlanLoading } = useDailyPlan(); +export function FutureTasks({ profile }: { profile: any }) { + const { deleteDailyPlan, deleteDailyPlanLoading, futurePlans } = useDailyPlan(); const canSeeActivity = useCanSeeActivityScreen(); return (
    - {filteredPlans.length > 0 ? ( + {futurePlans.length > 0 ? ( - {filteredPlans.map((plan) => ( + {futurePlans.map((plan) => ( !plan.date?.toString()?.startsWith(new Date()?.toISOString().split('T')[0])) - - // Exclude future plans - .filter((plan) => { - const planDate = new Date(plan.date); - const today = new Date(); - today.setHours(23, 59, 59, 0); // Set today time to exclude timestamps in comparization - return planDate.getTime() <= today.getTime(); - }) - .sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()) - .map((plan) => ({ - ...plan, - // Include only no completed tasks - tasks: plan.tasks?.filter((task) => task.status !== 'completed') - })) - .filter((plan) => plan.tasks?.length && plan.tasks.length > 0); +export function Outstanding({ profile }: { profile: any }) { + const { outstandingPlans } = useDailyPlan(); return (
    - {filteredPlans?.length > 0 ? ( + {outstandingPlans?.length > 0 ? ( new Date(plan.date).toISOString().split('T')[0])} + defaultValue={outstandingPlans?.map((plan) => new Date(plan.date).toISOString().split('T')[0])} > - {filteredPlans?.map((plan) => ( + {outstandingPlans?.map((plan) => ( new Date(b.date).getTime() - new Date(a.date).getTime()); - const filteredPlans = descSortedPlans.filter((plan) => { - const planDate = new Date(plan.date); - const today = new Date(); - today.setHours(0, 0, 0, 0); // Set today time to exclude timestamps in comparization - return planDate.getTime() < today.getTime(); - }); +export function PastTasks({ profile }: { profile: any }) { + const { pastPlans } = useDailyPlan(); return (
    - {filteredPlans?.length > 0 ? ( + {pastPlans?.length > 0 ? ( - {filteredPlans?.map((plan) => ( + {pastPlans?.map((plan) => ( member.employee?.user?.id == user?.id); const canSeeActivity = profile.userProfile?.id === user?.id || isManagerConnectedUser != -1; @@ -188,7 +196,8 @@ export function useTaskFilter(profile: I_UserProfilePage) { onResetStatusFilter, applyStatusFilder, tasksGrouped: profile.tasksGrouped, - outclickFilterCard + outclickFilterCard, + profileDailyPlans }; } @@ -228,7 +237,9 @@ export function TaskFilter({ className, hook, profile }: IClassName & Props) { ref={hook.outclickFilterCard.targetEl} > {/* {hook.filterType !== undefined && } */} - {hook.filterType === 'status' && } + {hook.filterType === 'status' && ( + + )} {hook.filterType === 'search' && ( + {hook.tab === 'dailyplan' && } +
    ))} @@ -66,26 +78,13 @@ export function UserProfilePlans() { ); } -function AllPlans({ - plans, - profile, - currentTab = 'All Tasks' -}: { - plans: IDailyPlan[]; - profile: any; - currentTab?: FilterTabs; -}) { +function AllPlans({ profile, currentTab = 'All Tasks' }: { profile: any; currentTab?: FilterTabs }) { // Filter plans let filteredPlans: IDailyPlan[] = []; + const { deleteDailyPlan, deleteDailyPlanLoading, sortedPlans, todayPlan } = useDailyPlan(); - filteredPlans = [...plans].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); - - if (currentTab === 'Today Tasks') - filteredPlans = [...plans].filter((plan) => - plan.date?.toString()?.startsWith(new Date()?.toISOString().split('T')[0]) - ); - - const { deleteDailyPlan, deleteDailyPlanLoading } = useDailyPlan(); + filteredPlans = sortedPlans; + if (currentTab === 'Today Tasks') filteredPlans = todayPlan; const canSeeActivity = useCanSeeActivityScreen(); diff --git a/apps/web/styles/globals.css b/apps/web/styles/globals.css index d2d09f802..44d638c3a 100644 --- a/apps/web/styles/globals.css +++ b/apps/web/styles/globals.css @@ -312,3 +312,31 @@ html.dark { transform: rotate(-90deg); transform-origin: 50% 50%; } + +@layer utilities { + @layer responsive { + .no-scrollbar::-webkit-scrollbar { + display: block; + } + + .no-scrollbar:hover::-webkit-scrollbar { + display: block; + width: 8px; + height: 5px; + @apply dark:bg-[#606062] bg-gray-300; + } + + .no-scrollbar::-webkit-scrollbar-thumb { + @apply dark:bg-dark--theme-light bg-gray-400; + } + + .no-scrollbar { + -ms-overflow-style: none; + scrollbar-width: none; + } + .no-scrollbar:hover { + -ms-overflow-style: auto; + scrollbar-width: auto; + } + } +} From de73a3f0177f7f50a938f23efd45ee1bc21209f6 Mon Sep 17 00:00:00 2001 From: sutralia Date: Sat, 22 Jun 2024 00:53:01 +0700 Subject: [PATCH 05/14] feat: added translation function to server web --- apps/server-web/package.json | 7 +- apps/server-web/src/configs/i18nResource.ts | 18 ++++ .../src/locales/en/translation.json | 30 +++++++ .../src/locales/en/translation.missing.json | 1 + apps/server-web/src/main/preload.ts | 6 ++ apps/server-web/src/renderer/App.tsx | 9 ++ .../src/renderer/components/Popup.tsx | 8 +- .../src/renderer/components/Server.tsx | 10 ++- .../src/renderer/components/SideBar.tsx | 4 +- .../src/renderer/components/Updater.tsx | 20 +++-- apps/server-web/src/renderer/index.tsx | 8 +- .../server-web/src/renderer/pages/Setting.tsx | 31 ++++--- apps/server-web/src/renderer/preload.d.ts | 3 +- yarn.lock | 85 +++++++++++++------ 14 files changed, 177 insertions(+), 63 deletions(-) create mode 100644 apps/server-web/src/configs/i18nResource.ts create mode 100644 apps/server-web/src/locales/en/translation.json create mode 100644 apps/server-web/src/locales/en/translation.missing.json diff --git a/apps/server-web/package.json b/apps/server-web/package.json index 9a0c0265a..cf1099ab7 100644 --- a/apps/server-web/package.json +++ b/apps/server-web/package.json @@ -44,7 +44,12 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.16.0", - "electron-store": "^8.1.0" + "electron-store": "^8.1.0", + "i18next-browser-languagedetector": "^7.2.1", + "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" }, "devDependencies": { "electron": "28.1.0", diff --git a/apps/server-web/src/configs/i18nResource.ts b/apps/server-web/src/configs/i18nResource.ts new file mode 100644 index 000000000..5a577ea9b --- /dev/null +++ b/apps/server-web/src/configs/i18nResource.ts @@ -0,0 +1,18 @@ +import i18n from 'i18next'; +import resourcesToBackend from 'i18next-resources-to-backend'; + +i18n + .use( + resourcesToBackend((language: string, namespace: string) => import(`../locales/${language}/${namespace}.json`)) + ) + .init({ + debug: true, + fallbackLng: 'en', + interpolation: { + escapeValue: false // not needed for react as it escapes by default + } + }); +i18n.on('failedLoading', (lng, ns, msg) => console.error(msg)) +i18n.languages = ['en']; + +export default i18n; diff --git a/apps/server-web/src/locales/en/translation.json b/apps/server-web/src/locales/en/translation.json new file mode 100644 index 000000000..d6060d731 --- /dev/null +++ b/apps/server-web/src/locales/en/translation.json @@ -0,0 +1,30 @@ +{ + "MENU": { + "SERVER": "Server", + "UPDATER": "Updater", + "ABOUT": "About" + }, + "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 new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/apps/server-web/src/locales/en/translation.missing.json @@ -0,0 +1 @@ +{} diff --git a/apps/server-web/src/main/preload.ts b/apps/server-web/src/main/preload.ts index 07414a2c4..e05d384be 100644 --- a/apps/server-web/src/main/preload.ts +++ b/apps/server-web/src/main/preload.ts @@ -28,5 +28,11 @@ const electronHandler = { }; contextBridge.exposeInMainWorld('electron', electronHandler); +contextBridge.exposeInMainWorld('languageChange', { + language: (callback: any) => ipcRenderer.on('languageSignal', (_event, value) => callback(value)) +}) export type ElectronHandler = typeof electronHandler; +export type languageChange = { + language: (callback: any) => void +} diff --git a/apps/server-web/src/renderer/App.tsx b/apps/server-web/src/renderer/App.tsx index 77931220c..2b014af3e 100644 --- a/apps/server-web/src/renderer/App.tsx +++ b/apps/server-web/src/renderer/App.tsx @@ -1,9 +1,18 @@ +import { useState, useEffect } from 'react'; import { HashRouter as Router, Routes, Route } from 'react-router-dom'; import icon from '../../assets/icon.svg'; import './App.css'; import { Setting } from './pages/Setting'; +import i18next from 'i18next'; export default function App() { + const [language, setLanguage] = useState('en'); + useEffect(() => { + window.languageChange.language((value: any) => { + setLanguage(value); + }); + i18next.changeLanguage(language); + }, [language]); return ( diff --git a/apps/server-web/src/renderer/components/Popup.tsx b/apps/server-web/src/renderer/components/Popup.tsx index 441371db8..0aead7aa0 100644 --- a/apps/server-web/src/renderer/components/Popup.tsx +++ b/apps/server-web/src/renderer/components/Popup.tsx @@ -1,3 +1,4 @@ +import { useTranslation } from 'react-i18next'; type Props = { isShowPopup: boolean; modalAction: () => void; @@ -5,6 +6,7 @@ type Props = { message: string; }; export function Popup(props: Props) { + const { t } = useTranslation(); return ( diff --git a/apps/server-web/src/renderer/components/Server.tsx b/apps/server-web/src/renderer/components/Server.tsx index 2cedb0921..3550ca3ab 100644 --- a/apps/server-web/src/renderer/components/Server.tsx +++ b/apps/server-web/src/renderer/components/Server.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; interface IServerSetting { PORT: number; @@ -13,6 +14,7 @@ type Props = { }; export const ServerComponent = (props: Props) => { + const { t } = useTranslation(); const [serverSetting, setServerSetting] = useState( props.serverSetting, ); @@ -38,7 +40,7 @@ export const ServerComponent = (props: Props) => { className="block text-gray-500 font-bold md:text-left mb-1 md:mb-0 pr-4" htmlFor="inline-full-name" > - Port + {t('FORM.FIELDS.PORT')}
    @@ -58,7 +60,7 @@ export const ServerComponent = (props: Props) => { className="block text-gray-500 font-bold md:text-left mb-1 md:mb-0 pr-4" htmlFor="inline-full-name" > - Gauzy Api Server URL + {t('FORM.FIELDS.GAUZY_API_SERVER_URL')}
    @@ -78,7 +80,7 @@ export const ServerComponent = (props: Props) => { className="block text-gray-500 font-bold md:text-left mb-1 md:mb-0 pr-4" htmlFor="inline-full-name" > - Public Gauzy Api Server URL + {t('FORM.FIELDS.NEXT_PUBLIC_GAUZY_API_SERVER_URL')}
    @@ -99,7 +101,7 @@ export const ServerComponent = (props: Props) => { className="mx-auto mt-10 block rounded-xl border-4 border-transparent bg-blue-400 px-6 py-3 text-center text-base font-medium text-blue-100 outline-8 hover:outline hover:duration-300" type="submit" > - Save Setting + {t('FORM.BUTTON.SAVE_SETTING')}
    diff --git a/apps/server-web/src/renderer/components/SideBar.tsx b/apps/server-web/src/renderer/components/SideBar.tsx index 5fa963e52..b8d824122 100644 --- a/apps/server-web/src/renderer/components/SideBar.tsx +++ b/apps/server-web/src/renderer/components/SideBar.tsx @@ -1,4 +1,5 @@ import { Link } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; interface SideMenu { displayName: string; key: string; @@ -10,6 +11,7 @@ type Props = { menuChange: (key: string) => void; }; export function SideBar({ children, menus, menuChange }: Props) { + const { t } = useTranslation(); return (
    @@ -27,7 +29,7 @@ export function SideBar({ children, menus, menuChange }: Props) { > - {menu.displayName} + {t(`MENU.${menu.displayName}`)} diff --git a/apps/server-web/src/renderer/components/Updater.tsx b/apps/server-web/src/renderer/components/Updater.tsx index 9023e9ce2..dd227b397 100644 --- a/apps/server-web/src/renderer/components/Updater.tsx +++ b/apps/server-web/src/renderer/components/Updater.tsx @@ -1,4 +1,5 @@ import { EverTeamsLogo } from './svgs'; +import { useTranslation } from 'react-i18next'; interface UpdaterStates { state: @@ -11,18 +12,18 @@ interface UpdaterStates { | 'up-to-date'; data: any; label: - | 'Checking' - | 'Downloading' - | 'Quit and Install' - | 'Up to date' - | 'Update Available' - | 'Check For Update' - | 'Up to date'; + | 'CHECKING' + | 'DOWNLOADING' + | 'QUIT_N_INSTALL' + | 'UP_TO_DATE' + | 'UPDATE_AVAILABLE' + | 'CHECK_FOR_UPDATE'; } type PropsProgress = { updateStates: UpdaterStates; }; const ProgressComponent = (props: PropsProgress) => { + const { t } = useTranslation(); return (
    @@ -49,7 +50,7 @@ const ProgressComponent = (props: PropsProgress) => {
    {props.updateStates.state === 'downloading' && - `${props.updateStates.label} ${props.updateStates.data} %`} + `${t(`FORM.LABELS.${props.updateStates.label}`)} ${props.updateStates.data} %`} {props.updateStates.state !== 'downloading' && props.updateStates.label}
    @@ -63,6 +64,7 @@ type Props = { Popup: JSX.Element; }; export const UpdaterComponent = (props: Props) => { + const { t } = useTranslation(); return ( <>
    @@ -79,7 +81,7 @@ export const UpdaterComponent = (props: Props) => { {props.loading && ( )} - {!props.loading && props.updateStates.label} + {!props.loading && t(`FORM.LABELS.${props.updateStates.label}`)}
    diff --git a/apps/server-web/src/renderer/index.tsx b/apps/server-web/src/renderer/index.tsx index f8afd7ccb..4071c0f9f 100644 --- a/apps/server-web/src/renderer/index.tsx +++ b/apps/server-web/src/renderer/index.tsx @@ -1,6 +1,12 @@ import { createRoot } from 'react-dom/client'; import App from './App'; +import { I18nextProvider } from 'react-i18next'; +import i18n from '../configs/i18nResource'; const container = document.getElementById('root') as HTMLElement; const root = createRoot(container); -root.render(); +root.render( + + + , +); diff --git a/apps/server-web/src/renderer/pages/Setting.tsx b/apps/server-web/src/renderer/pages/Setting.tsx index 70dd58ae6..36fee091d 100644 --- a/apps/server-web/src/renderer/pages/Setting.tsx +++ b/apps/server-web/src/renderer/pages/Setting.tsx @@ -25,13 +25,12 @@ interface UpdaterStates { | 'up-to-date'; data: any; label: - | 'Checking' - | 'Downloading' - | 'Quit and Install' - | 'Up to date' - | 'Update Available' - | 'Check For Update' - | 'Up to date'; + | 'CHECKING' + | 'DOWNLOADING' + | 'QUIT_N_INSTALL' + | 'UP_TO_DATE' + | 'UPDATE_AVAILABLE' + | 'CHECK_FOR_UPDATE'; } interface IServerSetting { @@ -48,17 +47,17 @@ interface IPopup { export function Setting() { const [menus, setMenu] = useState([ { - displayName: 'Server', + displayName: 'SERVER', key: 'server', isActive: true, }, { - displayName: 'Updater', + displayName: 'UPDATER', key: 'updater', isActive: false, }, { - displayName: 'About', + displayName: 'ABOUT', key: 'about', isActive: false, }, @@ -67,7 +66,7 @@ export function Setting() { const [updateStates, setUpdateState] = useState({ state: 'not-started', data: null, - label: 'Check For Update', + label: 'CHECK_FOR_UPDATE', }); const [loading, setLoading] = useState(false); @@ -187,21 +186,21 @@ export function Setting() { setUpdateState({ state: 'update-available', data: null, - label: 'Update Available', + label: 'UPDATE_AVAILABLE', }); break; case SettingPageTypeMessage.downloadingUpdate: setUpdateState({ state: 'downloading', data: arg.data.percent, - label: 'Downloading', + label: 'DOWNLOADING', }); break; case SettingPageTypeMessage.downloaded: setUpdateState({ state: 'downloaded', data: null, - label: 'Quit and Install', + label: 'QUIT_N_INSTALL', }); setLoading(false); break; @@ -209,7 +208,7 @@ export function Setting() { setUpdateState({ state: 'error', data: arg.data.message, - label: 'Check For Update', + label: 'CHECK_FOR_UPDATE', }); setLoading(false); setPopupUpdater({ @@ -221,7 +220,7 @@ export function Setting() { setUpdateState({ state: 'up-to-date', data: null, - label: 'Up to date', + label: 'UP_TO_DATE', }); setLoading(false); break; diff --git a/apps/server-web/src/renderer/preload.d.ts b/apps/server-web/src/renderer/preload.d.ts index 53cc2d7b1..be1fdc3cc 100644 --- a/apps/server-web/src/renderer/preload.d.ts +++ b/apps/server-web/src/renderer/preload.d.ts @@ -1,9 +1,10 @@ -import { ElectronHandler } from '../main/preload'; +import { ElectronHandler, languageChange } from '../main/preload'; declare global { // eslint-disable-next-line no-unused-vars interface Window { electron: ElectronHandler; + languageChange: languageChange; } } diff --git a/yarn.lock b/yarn.lock index bc2d2124f..3745a1c1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1228,6 +1228,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.23.9": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" + integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.22.5", "@babel/template@^7.3.3": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" @@ -15141,6 +15148,13 @@ html-minifier-terser@^6.0.2: relateurl "^0.2.7" terser "^5.10.0" +html-parse-stringify@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" + integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== + dependencies: + void-elements "3.1.0" + html-webpack-plugin@^5.5.3: version "5.6.0" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz#50a8fa6709245608cb00e811eacecb8e0d7b7ea0" @@ -15368,11 +15382,31 @@ husky@^9.0.11: resolved "https://registry.yarnpkg.com/husky/-/husky-9.0.11.tgz#fc91df4c756050de41b3e478b2158b87c1e79af9" integrity sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw== +i18next-browser-languagedetector@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.2.1.tgz#1968196d437b4c8db847410c7c33554f6c448f6f" + integrity sha512-h/pM34bcH6tbz8WgGXcmWauNpQupCGr25XPp9cZwZInR9XHSjIFDYp1SIok7zSPsTOMxdvuLyu86V+g2Kycnfw== + dependencies: + "@babel/runtime" "^7.23.2" + +i18next-electron-fs-backend@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/i18next-electron-fs-backend/-/i18next-electron-fs-backend-3.0.2.tgz#2fe8352478716151473e320dd13f3f1ad628ff55" + integrity sha512-KRP+4ORx0WG31qHvMNUpI4CytEQFAkFtXnrZ9/NBXH6k/DcKU3IdB573Zl+L+lR4GA6PCKyBX89mqrUhStoItA== + dependencies: + lodash.clonedeep "^4.5.0" + lodash.merge "^4.6.2" + i18next-fs-backend@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/i18next-fs-backend/-/i18next-fs-backend-2.2.0.tgz#016c865344632a666ea80653deae466fbfa6042c" integrity sha512-VOPHhdDX0M/csRqEw+9Ectpf6wvTIg1MZDfAHxc3JKnAlJz7fcZSAKAeyDohOq0xuLx57esYpJopIvBaRb0Bag== +i18next-fs-backend@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/i18next-fs-backend/-/i18next-fs-backend-2.3.1.tgz#0c7d2459ff4a039e2b3228131809fbc0e74ff1a8" + integrity sha512-tvfXskmG/9o+TJ5Fxu54sSO5OkY6d+uMn+K6JiUGLJrwxAVfer+8V3nU8jq3ts9Pe5lXJv4b1N7foIjJ8Iy2Gg== + i18next-http-backend@^2.2.0: version "2.2.2" resolved "https://registry.yarnpkg.com/i18next-http-backend/-/i18next-http-backend-2.2.2.tgz#3ee16dfe5fe33524ec8925d4f0bf1508ebbbfadf" @@ -15380,6 +15414,13 @@ i18next-http-backend@^2.2.0: dependencies: cross-fetch "3.1.6" +i18next-resources-to-backend@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/i18next-resources-to-backend/-/i18next-resources-to-backend-1.2.1.tgz#fded121e63e3139ce839c9901b9449dbbea7351d" + integrity sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw== + dependencies: + "@babel/runtime" "^7.23.2" + i18next@^22.4.15: version "22.5.1" resolved "https://registry.yarnpkg.com/i18next/-/i18next-22.5.1.tgz#99df0b318741a506000c243429a7352e5f44d424" @@ -21561,6 +21602,14 @@ react-hook-form@^7.42.1: resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.46.0.tgz#dba3491e4bc0968346e5615d9d669af780b47cd4" integrity sha512-sc22pXwuKgbWBR5/EYWOVoFw4i/w893tDRUgQY2/Xb7wlpajJBrqAMFhb4z1CDhZ0TSFFfX62+iKx3gCXnCHHw== +react-i18next@^14.1.0: + version "14.1.2" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-14.1.2.tgz#cd57a755f25a32a5fcc3dbe546cf3cc62b4f3ebd" + integrity sha512-FSIcJy6oauJbGEXfhUgVeLzvWBhIBIS+/9c6Lj4niwKZyGaGb4V4vUbATXSlsHJDXXB+ociNxqFNiFuV1gmoqg== + dependencies: + "@babel/runtime" "^7.23.9" + html-parse-stringify "^3.0.1" + react-icons@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-5.2.0.tgz#790c92d5f1888ef2dbbd3468c553fd4bb5c8bf7e" @@ -23434,16 +23483,7 @@ string-to-color@^2.2.2: lodash.words "^4.2.0" rgb-hex "^3.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -23553,14 +23593,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -25075,6 +25108,11 @@ vm-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +void-elements@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== + vscode-languageserver-textdocument@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz#0822a000e7d4dc083312580d7575fe9e3ba2e2bf" @@ -25559,7 +25597,7 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -25577,15 +25615,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From a47039ead532def3f96010abd6683c100183f7f7 Mon Sep 17 00:00:00 2001 From: sutralia Date: Sun, 23 Jun 2024 01:44:20 +0700 Subject: [PATCH 06/14] feat: enable function to change languages --- apps/server-web/package.json | 3 +- .../server-web/src/configs/i18n.mainconfig.ts | 24 +++++++ apps/server-web/src/configs/i18nResource.ts | 2 +- .../src/locales/bg/translation.json | 39 +++++++++++ .../src/locales/bg/translation.missing.json | 1 + .../src/locales/en/translation.json | 11 ++- .../src/locales/en/translation.missing.json | 7 +- apps/server-web/src/main/helpers/constant.ts | 6 +- apps/server-web/src/main/main.ts | 31 ++++++-- apps/server-web/src/main/tray.ts | 18 ++--- apps/server-web/src/renderer/App.css | 6 ++ .../src/renderer/components/General.tsx | 70 +++++++++++++++++++ .../src/renderer/components/index.ts | 1 + .../server-web/src/renderer/pages/Setting.tsx | 33 ++++++++- 14 files changed, 233 insertions(+), 19 deletions(-) create mode 100644 apps/server-web/src/configs/i18n.mainconfig.ts create mode 100644 apps/server-web/src/locales/bg/translation.json create mode 100644 apps/server-web/src/locales/bg/translation.missing.json create mode 100644 apps/server-web/src/renderer/components/General.tsx diff --git a/apps/server-web/package.json b/apps/server-web/package.json index cf1099ab7..816362152 100644 --- a/apps/server-web/package.json +++ b/apps/server-web/package.json @@ -239,7 +239,8 @@ }, "extraResources": [ "./assets/**", - "./release/app/dist/standalone/**" + "./release/app/dist/standalone/**", + "./src/locales" ], "publish": [ { diff --git a/apps/server-web/src/configs/i18n.mainconfig.ts b/apps/server-web/src/configs/i18n.mainconfig.ts new file mode 100644 index 000000000..9b4274a81 --- /dev/null +++ b/apps/server-web/src/configs/i18n.mainconfig.ts @@ -0,0 +1,24 @@ +import i18n from 'i18next'; +import backend from 'i18next-fs-backend'; +import { app } from 'electron'; + +import path from 'path'; +const prependPath = app.isPackaged + ? path.join(process.resourcesPath) + : path.join(__dirname, '..'); + +i18n.use(backend).init({ + backend: { + loadPath: prependPath + '/src/locales/{{lng}}/{{ns}}.json', + addPath: prependPath + '/src/locales/{{lng}}/{{ns}}.missing.json' + }, + debug: false, + ns: 'translation', + saveMissing: true, + saveMissingTo: 'current', + lng: 'en', + fallbackLng: false, // set to false when generating translation files locally + supportedLngs: ['en', 'bg'] +}); + +export default i18n; diff --git a/apps/server-web/src/configs/i18nResource.ts b/apps/server-web/src/configs/i18nResource.ts index 5a577ea9b..f093848d4 100644 --- a/apps/server-web/src/configs/i18nResource.ts +++ b/apps/server-web/src/configs/i18nResource.ts @@ -13,6 +13,6 @@ i18n } }); i18n.on('failedLoading', (lng, ns, msg) => console.error(msg)) -i18n.languages = ['en']; +i18n.languages = ['en', 'bg']; export default i18n; diff --git a/apps/server-web/src/locales/bg/translation.json b/apps/server-web/src/locales/bg/translation.json new file mode 100644 index 000000000..0c7b3f5c9 --- /dev/null +++ b/apps/server-web/src/locales/bg/translation.json @@ -0,0 +1,39 @@ +{ + "MENU": { + "SERVER": "сървър", + "UPDATER": "Актуализатор", + "ABOUT": "относно", + "SERVER_START": "Започнете", + "SERVER_STOP": "Спри се", + "APP_SETTING": "Настройка", + "APP_ABOUT": "относно", + "APP_QUIT": "Откажете се", + "GENERAL": "Общ", + "STATUS": "Статус", + "STARTED": "започна", + "STOPPED": "Спряна" + }, + "FORM": { + "FIELDS": { + "PORT": "ПРИСТАНИЩЕ", + "GAUZY_API_SERVER_URL": "Gauzy API сървър Url", + "NEXT_PUBLIC_GAUZY_API_SERVER_URL": "Публичен Gauzy API сървър Url" + }, + "BUTTON": { + "SAVE_SETTING": "Запазване на настройката", + "OK": "Добре" + }, + "LABELS": { + "CHECKING": "Проверка", + "DOWNLOADING": "Изтегля се", + "QUIT_N_INSTALL": "Излезте и инсталирайте", + "UP_TO_DATE": "Актуална", + "UPDATE_AVAILABLE": "Налична актуализация", + "CHECK_FOR_UPDATE": "Проверка за актуализация" + } + }, + "MESSAGE": { + "SUCCESS": "Успех", + "ERROR": "Грешка" + } +} diff --git a/apps/server-web/src/locales/bg/translation.missing.json b/apps/server-web/src/locales/bg/translation.missing.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/apps/server-web/src/locales/bg/translation.missing.json @@ -0,0 +1 @@ +{} diff --git a/apps/server-web/src/locales/en/translation.json b/apps/server-web/src/locales/en/translation.json index d6060d731..a132432b7 100644 --- a/apps/server-web/src/locales/en/translation.json +++ b/apps/server-web/src/locales/en/translation.json @@ -2,7 +2,16 @@ "MENU": { "SERVER": "Server", "UPDATER": "Updater", - "ABOUT": "About" + "ABOUT": "About", + "SERVER_START": "Start", + "SERVER_STOP": "Stop", + "APP_SETTING": "Setting", + "APP_ABOUT": "About", + "APP_QUIT": "Quit", + "GENERAL": "General", + "STATUS": "Status", + "STARTED": "Started", + "STOPPED": "Stopped" }, "FORM": { "FIELDS": { diff --git a/apps/server-web/src/locales/en/translation.missing.json b/apps/server-web/src/locales/en/translation.missing.json index 0967ef424..eca021d2c 100644 --- a/apps/server-web/src/locales/en/translation.missing.json +++ b/apps/server-web/src/locales/en/translation.missing.json @@ -1 +1,6 @@ -{} +{ + "MENU": { + "STATUS": "MENU.STATUS", + "STOPPED": "MENU.STOPPED" + } +} \ 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 1a90e6ae5..aef8a0245 100644 --- a/apps/server-web/src/main/helpers/constant.ts +++ b/apps/server-web/src/main/helpers/constant.ts @@ -10,7 +10,8 @@ export const EventLists = { UPDATE_NOT_AVAILABLE: 'UPDATE_NOT_AVAILABLE', UPDATE_PROGRESS: 'UPDATE_PROGRESS', UPDATE_DOWNLOADED: 'UPDATE_DOWNLOADED', - UPDATE_CANCELLED: 'UPDATE_CANCELLED' + UPDATE_CANCELLED: 'UPDATE_CANCELLED', + CHANGE_LANGUAGE: 'CHANGE_LANGUAGE' } export const SettingPageTypeMessage = { @@ -25,5 +26,6 @@ export const EventLists = { upToDate: 'up-to-date', mainResponse: 'main-response', showVersion: 'show-version', - selectMenu: 'select-menu' + selectMenu: 'select-menu', + langChange: 'lang' } diff --git a/apps/server-web/src/main/main.ts b/apps/server-web/src/main/main.ts index a2bba8e40..2edbe9e5a 100644 --- a/apps/server-web/src/main/main.ts +++ b/apps/server-web/src/main/main.ts @@ -7,6 +7,9 @@ import { defaultTrayMenuItem, _initTray, updateTrayMenu } from './tray'; import { EventLists, SettingPageTypeMessage } from './helpers/constant'; import { resolveHtmlPath } from './util'; import Updater from './updater'; +import { mainBindings } from 'i18next-electron-fs-backend'; +import i18nextMainBackend from '../configs/i18n.mainconfig'; +import fs from 'fs'; const eventEmitter = new EventEmitter(); @@ -23,8 +26,12 @@ let isServerRun: boolean; let tray:Tray; let settingWindow: BrowserWindow | null = null; const updater = new Updater(eventEmitter); +i18nextMainBackend.on('initialized', () => { + i18nextMainBackend.changeLanguage('en'); + i18nextMainBackend.off('initialized'); // Remove listener to this event as it's not needed anymore +}); -const trayMenuItems = defaultTrayMenuItem(eventEmitter); +let trayMenuItems: any = []; const RESOURCES_PATH = app.isPackaged ? path.join(process.resourcesPath, 'assets/icons/gauzy') @@ -103,7 +110,7 @@ const createWindow = async () => { const url = resolveHtmlPath('index.html', 'setting'); settingWindow.loadURL(url); - + mainBindings(ipcMain, settingWindow , fs); settingWindow.on('closed', () => { settingWindow = null; }); @@ -149,7 +156,15 @@ const SendMessageToSettingWindow = (type: string, data: any) => { const onInitApplication = () => { LocalStore.setDefaultServerConfig(); // check and set default config + trayMenuItems = defaultTrayMenuItem(eventEmitter, i18nextMainBackend); tray = _initTray(trayMenuItems, getAssetPath('icon.png')); + i18nextMainBackend.on('languageChanged', (lng) => { + console.log('change lang', lng); + if (i18nextMainBackend.isInitialized) { + trayMenuItems = defaultTrayMenuItem(eventEmitter, i18nextMainBackend); + updateTrayMenu('none', {}, eventEmitter, tray, trayMenuItems); + } + }); eventEmitter.on(EventLists.webServerStart, async () => { updateTrayMenu('SERVER_START', { enabled: false }, eventEmitter, tray, trayMenuItems); isServerRun = true; @@ -165,7 +180,7 @@ const onInitApplication = () => { console.log(EventLists.webServerStarted) updateTrayMenu('SERVER_START', { enabled: false }, eventEmitter, tray, trayMenuItems); updateTrayMenu('SERVER_STOP', { enabled: true }, eventEmitter, tray, trayMenuItems); - updateTrayMenu('SERVER_STATUS', { label: 'Status: Started' }, eventEmitter, tray, trayMenuItems); + updateTrayMenu('SERVER_STATUS', { label: `${i18nextMainBackend.t('MENU.STATUS')}: ${i18nextMainBackend.t('MENU.STARTED')}` }, eventEmitter, tray, trayMenuItems); isServerRun = true; }) @@ -173,7 +188,7 @@ const onInitApplication = () => { console.log(EventLists.webServerStopped); updateTrayMenu('SERVER_STOP', { enabled: false }, eventEmitter, tray, trayMenuItems); updateTrayMenu('SERVER_START', { enabled: true }, eventEmitter, tray, trayMenuItems); - updateTrayMenu('SERVER_STATUS', { label: 'Status: Stopped' }, eventEmitter, tray, trayMenuItems); + updateTrayMenu('SERVER_STATUS', { label: `${i18nextMainBackend.t('MENU.STATUS')}: ${i18nextMainBackend.t('MENU.STOPPED')}` }, eventEmitter, tray, trayMenuItems); isServerRun = false; }) @@ -218,6 +233,10 @@ const onInitApplication = () => { console.log('UPDATE_CANCELLED', data); }) + eventEmitter.on(EventLists.CHANGE_LANGUAGE, (data) => { + i18nextMainBackend.changeLanguage(data.code) + }) + eventEmitter.on(EventLists.gotoAbout, async () => { if (!settingWindow) { await createWindow(); @@ -262,6 +281,10 @@ ipcMain.on('setting-page', (event, arg) => { const currentVersion = app.getVersion(); 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}) + break; default: break; } diff --git a/apps/server-web/src/main/tray.ts b/apps/server-web/src/main/tray.ts index 7e1baed1d..5c737d32d 100644 --- a/apps/server-web/src/main/tray.ts +++ b/apps/server-web/src/main/tray.ts @@ -2,9 +2,9 @@ import { app, NativeImage, nativeImage, Menu, Tray } from 'electron'; import path from 'path'; import { EventEmitter } from 'events'; import { EventLists } from './helpers/constant'; +import i18n from 'i18next'; export const _initTray = (contextMenu:any, icon:string): Tray => { - const iconNativePath: NativeImage = nativeImage.createFromPath(icon); iconNativePath.resize({ width: 16, height: 16 }) const tray = new Tray(iconNativePath); @@ -12,22 +12,22 @@ export const _initTray = (contextMenu:any, icon:string): Tray => { return tray; } -export const defaultTrayMenuItem = (eventEmitter: EventEmitter) => { +export const defaultTrayMenuItem = (eventEmitter: EventEmitter, i18nextMainBackend: typeof i18n) => { const contextMenu = [ { id: 'SERVER_STATUS', - label: 'Status: Stopped', + label: `${i18nextMainBackend.t('MENU.STATUS')}: ${i18nextMainBackend.t('MENU.STOPPED')}`, }, { id: 'SERVER_START', - label: 'Start', + label: i18nextMainBackend.t('MENU.SERVER_START'), async click() { eventEmitter.emit(EventLists.webServerStart); } }, { id: 'SERVER_STOP', - label: 'Stop', + label: i18nextMainBackend.t('MENU.SERVER_STOP'), enabled: false, async click() { eventEmitter.emit(EventLists.webServerStop); @@ -35,21 +35,21 @@ export const defaultTrayMenuItem = (eventEmitter: EventEmitter) => { }, { id: 'APP_SETTING', - label: 'Settings', + label: i18nextMainBackend.t('MENU.APP_SETTING'), async click() { eventEmitter.emit(EventLists.gotoSetting); } }, { id: 'APP_ABOUT', - label: 'About Gauzy Web Server', + label: i18nextMainBackend.t('MENU.APP_ABOUT'), async click() { eventEmitter.emit(EventLists.gotoAbout) } }, { id: 'APP_QUIT', - label: 'Quit', + label: i18nextMainBackend.t('MENU.APP_QUIT'), click() { app.quit(); } @@ -64,5 +64,7 @@ export const updateTrayMenu = (menuItem: string, context: { label?: string, enab contextMenuItems[menuIdx] = {...contextMenuItems[menuIdx], ...context}; console.log(contextMenuItems) tray.setContextMenu(Menu.buildFromTemplate(contextMenuItems)); + } else { + tray.setContextMenu(Menu.buildFromTemplate(contextMenuItems)) } } diff --git a/apps/server-web/src/renderer/App.css b/apps/server-web/src/renderer/App.css index 9bb37ee65..f92d6b096 100644 --- a/apps/server-web/src/renderer/App.css +++ b/apps/server-web/src/renderer/App.css @@ -2,3 +2,9 @@ @tailwind base; @tailwind components; @tailwind utilities; + +.dropdown:focus-within .dropdown-menu { + opacity:1; + transform: translate(0) scale(1); + visibility: visible; + } diff --git a/apps/server-web/src/renderer/components/General.tsx b/apps/server-web/src/renderer/components/General.tsx new file mode 100644 index 000000000..13ef779e2 --- /dev/null +++ b/apps/server-web/src/renderer/components/General.tsx @@ -0,0 +1,70 @@ +import { Link } from 'react-router-dom'; +interface Languages { + code: string; + label: string; +} +type Props = { + langs: Languages[]; + onChange: (lang: any) => void; +}; +export function GeneralComponent(props: Props) { + return ( + <> +
    +
    +
    +
    + + + +
    + +
    +
    +
    +
    +
    + + ); +} diff --git a/apps/server-web/src/renderer/components/index.ts b/apps/server-web/src/renderer/components/index.ts index b23be57ba..24d9bdff1 100644 --- a/apps/server-web/src/renderer/components/index.ts +++ b/apps/server-web/src/renderer/components/index.ts @@ -3,3 +3,4 @@ export * from './Popup'; export * from './Server'; export * from './SideBar'; export * from './Updater'; +export * from './General'; diff --git a/apps/server-web/src/renderer/pages/Setting.tsx b/apps/server-web/src/renderer/pages/Setting.tsx index 36fee091d..005b17e89 100644 --- a/apps/server-web/src/renderer/pages/Setting.tsx +++ b/apps/server-web/src/renderer/pages/Setting.tsx @@ -6,6 +6,7 @@ import { ServerComponent, UpdaterComponent, AboutComponent, + GeneralComponent, } from '../components'; interface SideMenu { @@ -44,12 +45,22 @@ interface IPopup { isShow: boolean; } +interface Languages { + code: string; + label: string; +} + export function Setting() { const [menus, setMenu] = useState([ + { + displayName: 'GENERAL', + key: 'general', + isActive: true, + }, { displayName: 'SERVER', key: 'server', - isActive: true, + isActive: false, }, { displayName: 'UPDATER', @@ -63,6 +74,17 @@ export function Setting() { }, ]); + const [langs, setLangs] = useState([ + { + code: 'en', + label: 'English', + }, + { + code: 'bg', + label: 'Bulgarian', + }, + ]); + const [updateStates, setUpdateState] = useState({ state: 'not-started', data: null, @@ -96,6 +118,11 @@ export function Setting() { setMenu(newMenu); }; + const changeLanguage = (lang: Languages) => { + console.log(lang); + sendingMessageToMain(lang.code, SettingPageTypeMessage.langChange); + }; + const sendingMessageToMain = (data: any, type: string) => { window.electron.ipcRenderer.sendMessage('setting-page', { type, @@ -176,6 +203,10 @@ export function Setting() { sendingMessageToMain({}, SettingPageTypeMessage.showVersion); return ; } + + if (activeMenu() === 'general') { + return ; + } return ; }; From 04d9e90d4b73e7b2a29a796f4d4d375630d5870b Mon Sep 17 00:00:00 2001 From: sutralia Date: Sun, 23 Jun 2024 17:55:30 +0700 Subject: [PATCH 07/14] feat: enable function translate server web --- .../server-web/src/configs/i18n.mainconfig.ts | 2 +- .../src/locales/bg/translation.json | 5 +- .../src/locales/en/translation.json | 5 +- .../src/main/helpers/interfaces/i-server.ts | 13 ++++- .../helpers/services/libs/desktop-store.ts | 25 +++++++-- .../main/helpers/services/libs/server-task.ts | 20 +++---- apps/server-web/src/main/main.ts | 56 +++++++++++-------- apps/server-web/src/main/preload.ts | 2 +- apps/server-web/src/main/tray.ts | 31 ++++++---- apps/server-web/src/renderer/App.tsx | 1 - .../src/renderer/components/General.tsx | 7 ++- .../src/renderer/components/Updater.tsx | 3 +- .../server-web/src/renderer/pages/Setting.tsx | 16 ++++-- 13 files changed, 119 insertions(+), 67 deletions(-) diff --git a/apps/server-web/src/configs/i18n.mainconfig.ts b/apps/server-web/src/configs/i18n.mainconfig.ts index 9b4274a81..e01d9d288 100644 --- a/apps/server-web/src/configs/i18n.mainconfig.ts +++ b/apps/server-web/src/configs/i18n.mainconfig.ts @@ -5,7 +5,7 @@ import { app } from 'electron'; import path from 'path'; const prependPath = app.isPackaged ? path.join(process.resourcesPath) - : path.join(__dirname, '..'); + : path.join(__dirname, '..', '..'); i18n.use(backend).init({ backend: { diff --git a/apps/server-web/src/locales/bg/translation.json b/apps/server-web/src/locales/bg/translation.json index 0c7b3f5c9..30d6df466 100644 --- a/apps/server-web/src/locales/bg/translation.json +++ b/apps/server-web/src/locales/bg/translation.json @@ -9,9 +9,8 @@ "APP_ABOUT": "относно", "APP_QUIT": "Откажете се", "GENERAL": "Общ", - "STATUS": "Статус", - "STARTED": "започна", - "STOPPED": "Спряна" + "SERVER_STATUS_STOPPED": "Статус: Спряна", + "SERVER_STATUS_STARTED": "Статус: започна" }, "FORM": { "FIELDS": { diff --git a/apps/server-web/src/locales/en/translation.json b/apps/server-web/src/locales/en/translation.json index a132432b7..e91511fb9 100644 --- a/apps/server-web/src/locales/en/translation.json +++ b/apps/server-web/src/locales/en/translation.json @@ -9,9 +9,8 @@ "APP_ABOUT": "About", "APP_QUIT": "Quit", "GENERAL": "General", - "STATUS": "Status", - "STARTED": "Started", - "STOPPED": "Stopped" + "SERVER_STATUS_STOPPED": "Status: Stopped", + "SERVER_STATUS_STARTED": "Status: Started" }, "FORM": { "FIELDS": { 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 ec50d5aba..c3a27f7b7 100644 --- a/apps/server-web/src/main/helpers/interfaces/i-server.ts +++ b/apps/server-web/src/main/helpers/interfaces/i-server.ts @@ -1,6 +1,15 @@ -export interface WebServer { +interface GeneralConfig { + lang: string + [key: string]: any +} + +interface ServerConfig { PORT: number; NEXT_PUBLIC_GAUZY_API_SERVER_URL: string; GAUZY_API_SERVER_URL: string; [key: string]: any; -} \ No newline at end of file +} +export interface WebServer { + server?: ServerConfig; + general?: GeneralConfig; +} 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 275863cd1..68d2f71f7 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 @@ -2,13 +2,21 @@ import Store from 'electron-store'; import { WebServer } from '../../interfaces'; const store = new Store(); export const LocalStore = { - getStore: (source: string) => { + getStore: (source: string | 'config'): WebServer | any => { return store.get(source); }, updateConfigSetting: (values: WebServer) => { let config: WebServer | any = store.get('config'); - config = { ...config, ...values }; + Object.keys(values).forEach((key: string) => { + if (key === 'server') { + config[key] = {...config[key], ...values.server } + } + + if (key === 'general') { + config[key] = {...config[key], ...values.general } + } + }) store.set({ config }); @@ -17,11 +25,16 @@ export const LocalStore = { setDefaultServerConfig: () => { const defaultConfig: WebServer | any = store.get('config'); - if (!defaultConfig || !defaultConfig.PORT) { + if (!defaultConfig || !defaultConfig.server || !defaultConfig.general) { const config: WebServer = { - PORT: 3002, - GAUZY_API_SERVER_URL: 'htpp://localhost:3000', - NEXT_PUBLIC_GAUZY_API_SERVER_URL: 'http://localhost:3000' + server: { + PORT: 3002, + GAUZY_API_SERVER_URL: 'htpp://localhost:3000', + NEXT_PUBLIC_GAUZY_API_SERVER_URL: 'http://localhost:3000' + }, + general: { + lang: 'en' + } } 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 dba091b67..b3887f789 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 @@ -74,14 +74,14 @@ export abstract class ServerTask { console.log('Service created', service.pid); - service.stdout.on('data', (data: any) => { + service.stdout?.on('data', (data: any) => { const msg = data.toString(); this.loggerObserver.notify(msg); if (msg.includes(this.successMessage)) { const name = String(this.args.serviceName); this.stateObserver.notify(true); this.loggerObserver.notify( - `☣︎ ${name.toUpperCase()} server listen to ${this.config[`${name}Url`]}` + `☣︎ ${name.toUpperCase()} server listen to ${this.config.setting[`${name}Url`]}` ); resolve(); } @@ -92,7 +92,7 @@ export abstract class ServerTask { } }); - service.stderr.on('data', (data: any) => { + service.stderr?.on('data', (data: any) => { console.log('stderr:', data.toString()); this.loggerObserver.notify(data.toString()); }); @@ -111,7 +111,7 @@ export abstract class ServerTask { if (this.eventEmmitter) { this.eventEmmitter.emit(EventLists.webServerStarted); } - this.config.setting = { [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); @@ -123,13 +123,13 @@ export abstract class ServerTask { public kill(callHandleError = true): void { console.log('Kill Server Task'); try { - if (this.pid && this.config.setting[this.pid]) { - process.kill(this.config.setting[this.pid]); - delete this.config.setting[this.pid]; + if (this.pid && this.config.setting.server[this.pid]) { + process.kill(this.config.setting.server[this.pid]); + delete this.config.setting.server[this.pid]; this.stateObserver.notify(false); - this.loggerObserver.notify(`[${this.pid.toUpperCase()}-${this.config.setting[this.pid]}]: stopped`); + this.loggerObserver.notify(`[${this.pid.toUpperCase()}-${this.config.setting.server[this.pid]}]: stopped`); } - } catch (error) { + } catch (error: any) { if (callHandleError) { if (error.code === 'ESRCH') { error.message = `ERROR: Could not terminate the process [${this.pid}]. It was not running: ${error}`; @@ -140,7 +140,7 @@ export abstract class ServerTask { } public get running(): boolean { - return this.isRunning && !!this.config.setting[this.pid]; + return this.isRunning && !!this.config.setting.server[this.pid]; } public async restart(): Promise { diff --git a/apps/server-web/src/main/main.ts b/apps/server-web/src/main/main.ts index 2edbe9e5a..b291b57ea 100644 --- a/apps/server-web/src/main/main.ts +++ b/apps/server-web/src/main/main.ts @@ -3,13 +3,14 @@ import { app, ipcMain, Tray, dialog, BrowserWindow, shell } from 'electron'; import { DesktopServer } from './helpers/desktop-server'; import { LocalStore } from './helpers/services/libs/desktop-store'; import { EventEmitter } from 'events'; -import { defaultTrayMenuItem, _initTray, updateTrayMenu } from './tray'; +import { defaultTrayMenuItem, _initTray, updateTrayMenu, translateTrayMenu } from './tray'; import { EventLists, SettingPageTypeMessage } from './helpers/constant'; import { resolveHtmlPath } from './util'; import Updater from './updater'; import { mainBindings } from 'i18next-electron-fs-backend'; import i18nextMainBackend from '../configs/i18n.mainconfig'; import fs from 'fs'; +import { WebServer } from './helpers/interfaces'; const eventEmitter = new EventEmitter(); @@ -27,7 +28,9 @@ let tray:Tray; let settingWindow: BrowserWindow | null = null; const updater = new Updater(eventEmitter); i18nextMainBackend.on('initialized', () => { - i18nextMainBackend.changeLanguage('en'); + const config = LocalStore.getStore('config'); + const selectedLang = config && config.general && config.general.lang; + i18nextMainBackend.changeLanguage(selectedLang || 'en'); i18nextMainBackend.off('initialized'); // Remove listener to this event as it's not needed anymore }); @@ -142,9 +145,8 @@ const stopServer = async () => { }; const getEnvApi = () => { - const setting = LocalStore.getStore('config') - console.log(setting); - return setting; + const setting: WebServer = LocalStore.getStore('config') + return setting.server; }; const SendMessageToSettingWindow = (type: string, data: any) => { @@ -156,17 +158,16 @@ const SendMessageToSettingWindow = (type: string, data: any) => { const onInitApplication = () => { LocalStore.setDefaultServerConfig(); // check and set default config - trayMenuItems = defaultTrayMenuItem(eventEmitter, i18nextMainBackend); + trayMenuItems = trayMenuItems.length ? trayMenuItems : defaultTrayMenuItem(eventEmitter); tray = _initTray(trayMenuItems, getAssetPath('icon.png')); i18nextMainBackend.on('languageChanged', (lng) => { - console.log('change lang', lng); if (i18nextMainBackend.isInitialized) { - trayMenuItems = defaultTrayMenuItem(eventEmitter, i18nextMainBackend); - updateTrayMenu('none', {}, eventEmitter, tray, trayMenuItems); + trayMenuItems = trayMenuItems.length ? trayMenuItems : defaultTrayMenuItem(eventEmitter); + updateTrayMenu('none', {}, eventEmitter, tray, trayMenuItems, i18nextMainBackend); } }); eventEmitter.on(EventLists.webServerStart, async () => { - updateTrayMenu('SERVER_START', { enabled: false }, eventEmitter, tray, trayMenuItems); + updateTrayMenu('SERVER_START', { enabled: false }, eventEmitter, tray, trayMenuItems, i18nextMainBackend); isServerRun = true; await runServer(); }) @@ -178,17 +179,17 @@ const onInitApplication = () => { eventEmitter.on(EventLists.webServerStarted, () => { console.log(EventLists.webServerStarted) - updateTrayMenu('SERVER_START', { enabled: false }, eventEmitter, tray, trayMenuItems); - updateTrayMenu('SERVER_STOP', { enabled: true }, eventEmitter, tray, trayMenuItems); - updateTrayMenu('SERVER_STATUS', { label: `${i18nextMainBackend.t('MENU.STATUS')}: ${i18nextMainBackend.t('MENU.STARTED')}` }, eventEmitter, tray, trayMenuItems); + updateTrayMenu('SERVER_START', { enabled: false }, eventEmitter, tray, trayMenuItems, i18nextMainBackend); + updateTrayMenu('SERVER_STOP', { enabled: true }, eventEmitter, tray, trayMenuItems, i18nextMainBackend); + updateTrayMenu('SERVER_STATUS', { label: 'MENU.SERVER_STATUS_STARTED' }, eventEmitter, tray, trayMenuItems, i18nextMainBackend); isServerRun = true; }) eventEmitter.on(EventLists.webServerStopped, () => { console.log(EventLists.webServerStopped); - updateTrayMenu('SERVER_STOP', { enabled: false }, eventEmitter, tray, trayMenuItems); - updateTrayMenu('SERVER_START', { enabled: true }, eventEmitter, tray, trayMenuItems); - updateTrayMenu('SERVER_STATUS', { label: `${i18nextMainBackend.t('MENU.STATUS')}: ${i18nextMainBackend.t('MENU.STOPPED')}` }, eventEmitter, tray, trayMenuItems); + updateTrayMenu('SERVER_STOP', { enabled: false }, eventEmitter, tray, trayMenuItems, i18nextMainBackend); + updateTrayMenu('SERVER_START', { enabled: true }, eventEmitter, tray, trayMenuItems, i18nextMainBackend); + updateTrayMenu('SERVER_STATUS', { label: 'MENU.SERVER_STATUS_STOPPED' }, eventEmitter, tray, trayMenuItems, i18nextMainBackend); isServerRun = false; }) @@ -196,11 +197,14 @@ const onInitApplication = () => { if (!settingWindow) { await createWindow() } - const serverSetting = LocalStore.getStore('config'); + const serverSetting: WebServer = LocalStore.getStore('config'); console.log('setting data', serverSetting); settingWindow?.show(); settingWindow?.webContents.once('did-finish-load', () => { - SendMessageToSettingWindow(SettingPageTypeMessage.loadSetting, serverSetting); + setTimeout(()=> { + settingWindow?.webContents.send('languageSignal', serverSetting.general?.lang); + SendMessageToSettingWindow(SettingPageTypeMessage.loadSetting, serverSetting); + }, 50) }) }) @@ -234,7 +238,10 @@ const onInitApplication = () => { }) eventEmitter.on(EventLists.CHANGE_LANGUAGE, (data) => { - i18nextMainBackend.changeLanguage(data.code) + i18nextMainBackend.changeLanguage(data.code); + LocalStore.updateConfigSetting({general: { + lang: data.code + }}) }) eventEmitter.on(EventLists.gotoAbout, async () => { @@ -244,8 +251,11 @@ const onInitApplication = () => { const serverSetting = LocalStore.getStore('config'); settingWindow?.show(); settingWindow?.webContents.once('did-finish-load', () => { - SendMessageToSettingWindow(SettingPageTypeMessage.loadSetting, serverSetting); - SendMessageToSettingWindow(SettingPageTypeMessage.selectMenu, { key: 'about'}) + setTimeout(()=> { + SendMessageToSettingWindow(SettingPageTypeMessage.loadSetting, serverSetting); + settingWindow?.webContents.send('languageSignal', serverSetting.general?.lang); + SendMessageToSettingWindow(SettingPageTypeMessage.selectMenu, { key: 'about'}); + }, 100) }) }) } @@ -268,7 +278,9 @@ ipcMain.on('setting-page', (event, arg) => { console.log('main setting page', arg); switch (arg.type) { case SettingPageTypeMessage.saveSetting: - LocalStore.updateConfigSetting(arg.data); + LocalStore.updateConfigSetting({ + server: arg.data + }); event.sender.send('setting-page', { type: SettingPageTypeMessage.mainResponse, data: true }); break; case SettingPageTypeMessage.checkUpdate: diff --git a/apps/server-web/src/main/preload.ts b/apps/server-web/src/main/preload.ts index e05d384be..7ab4c0903 100644 --- a/apps/server-web/src/main/preload.ts +++ b/apps/server-web/src/main/preload.ts @@ -2,7 +2,7 @@ /* eslint no-unused-vars: off */ import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron'; -export type Channels = 'setting-page' | 'ipc-renderer'; +export type Channels = 'setting-page' | 'ipc-renderer' | 'language-set'; const electronHandler = { ipcRenderer: { diff --git a/apps/server-web/src/main/tray.ts b/apps/server-web/src/main/tray.ts index 5c737d32d..41fadb44d 100644 --- a/apps/server-web/src/main/tray.ts +++ b/apps/server-web/src/main/tray.ts @@ -12,22 +12,22 @@ export const _initTray = (contextMenu:any, icon:string): Tray => { return tray; } -export const defaultTrayMenuItem = (eventEmitter: EventEmitter, i18nextMainBackend: typeof i18n) => { +export const defaultTrayMenuItem = (eventEmitter: EventEmitter) => { const contextMenu = [ { id: 'SERVER_STATUS', - label: `${i18nextMainBackend.t('MENU.STATUS')}: ${i18nextMainBackend.t('MENU.STOPPED')}`, + label: 'MENU.SERVER_STATUS_STOPPED', }, { id: 'SERVER_START', - label: i18nextMainBackend.t('MENU.SERVER_START'), + label: 'MENU.SERVER_START', async click() { eventEmitter.emit(EventLists.webServerStart); } }, { id: 'SERVER_STOP', - label: i18nextMainBackend.t('MENU.SERVER_STOP'), + label: 'MENU.SERVER_STOP', enabled: false, async click() { eventEmitter.emit(EventLists.webServerStop); @@ -35,21 +35,21 @@ export const defaultTrayMenuItem = (eventEmitter: EventEmitter, i18nextMainBacke }, { id: 'APP_SETTING', - label: i18nextMainBackend.t('MENU.APP_SETTING'), + label: 'MENU.APP_SETTING', async click() { eventEmitter.emit(EventLists.gotoSetting); } }, { id: 'APP_ABOUT', - label: i18nextMainBackend.t('MENU.APP_ABOUT'), + label: 'MENU.APP_ABOUT', async click() { eventEmitter.emit(EventLists.gotoAbout) } }, { id: 'APP_QUIT', - label: i18nextMainBackend.t('MENU.APP_QUIT'), + label: 'MENU.APP_QUIT', click() { app.quit(); } @@ -58,13 +58,22 @@ export const defaultTrayMenuItem = (eventEmitter: EventEmitter, i18nextMainBacke return contextMenu; } -export const updateTrayMenu = (menuItem: string, context: { label?: string, enabled?: boolean}, eventEmitter: EventEmitter, tray: Tray, contextMenuItems: any) => { +export const updateTrayMenu = (menuItem: string, context: { label?: string, enabled?: boolean}, eventEmitter: EventEmitter, tray: Tray, contextMenuItems: any, i18nextMainBackend: typeof i18n) => { const menuIdx:number = contextMenuItems.findIndex((item: any) => item.id === menuItem); if (menuIdx > -1) { contextMenuItems[menuIdx] = {...contextMenuItems[menuIdx], ...context}; - console.log(contextMenuItems) - tray.setContextMenu(Menu.buildFromTemplate(contextMenuItems)); + const newMenu = [...contextMenuItems]; + tray.setContextMenu(Menu.buildFromTemplate(translateTrayMenu(i18nextMainBackend, newMenu))); } else { - tray.setContextMenu(Menu.buildFromTemplate(contextMenuItems)) + const newMenu = [...contextMenuItems]; + tray.setContextMenu(Menu.buildFromTemplate(translateTrayMenu(i18nextMainBackend, newMenu))) } } + +export const translateTrayMenu = (i18nextMainBackend: typeof i18n, contextMenu: any) => { + return contextMenu.map((menu: any) => { + const menuCopied = {...menu}; + menuCopied.label = i18nextMainBackend.t(menuCopied.label); + return menuCopied; + }) +} diff --git a/apps/server-web/src/renderer/App.tsx b/apps/server-web/src/renderer/App.tsx index 2b014af3e..40c981536 100644 --- a/apps/server-web/src/renderer/App.tsx +++ b/apps/server-web/src/renderer/App.tsx @@ -1,6 +1,5 @@ import { useState, useEffect } from 'react'; import { HashRouter as Router, Routes, Route } from 'react-router-dom'; -import icon from '../../assets/icon.svg'; import './App.css'; import { Setting } from './pages/Setting'; import i18next from 'i18next'; diff --git a/apps/server-web/src/renderer/components/General.tsx b/apps/server-web/src/renderer/components/General.tsx index 13ef779e2..9330568c5 100644 --- a/apps/server-web/src/renderer/components/General.tsx +++ b/apps/server-web/src/renderer/components/General.tsx @@ -6,8 +6,13 @@ interface Languages { type Props = { langs: Languages[]; onChange: (lang: any) => void; + lang: string; }; export function GeneralComponent(props: Props) { + const language = props.langs.find((lg) => lg.code === props.lang) || { + code: 'en', + label: 'English', + }; return ( <>
    @@ -22,7 +27,7 @@ export function GeneralComponent(props: Props) { aria-expanded="true" aria-controls="headlessui-menu-items-117" > - Language + {language.label} {
    {props.updateStates.state === 'downloading' && `${t(`FORM.LABELS.${props.updateStates.label}`)} ${props.updateStates.data} %`} - {props.updateStates.state !== 'downloading' && props.updateStates.label} + {props.updateStates.state !== 'downloading' && + t(`FORM.LABELS.${props.updateStates.label}`)}
    ); diff --git a/apps/server-web/src/renderer/pages/Setting.tsx b/apps/server-web/src/renderer/pages/Setting.tsx index 005b17e89..becabebb5 100644 --- a/apps/server-web/src/renderer/pages/Setting.tsx +++ b/apps/server-web/src/renderer/pages/Setting.tsx @@ -85,6 +85,8 @@ export function Setting() { }, ]); + const [lng, setLng] = useState('en'); + const [updateStates, setUpdateState] = useState({ state: 'not-started', data: null, @@ -121,6 +123,7 @@ export function Setting() { const changeLanguage = (lang: Languages) => { console.log(lang); sendingMessageToMain(lang.code, SettingPageTypeMessage.langChange); + setLng(lang.code); }; const sendingMessageToMain = (data: any, type: string) => { @@ -205,7 +208,9 @@ export function Setting() { } if (activeMenu() === 'general') { - return ; + return ( + + ); } return ; }; @@ -256,13 +261,14 @@ export function Setting() { setLoading(false); break; case SettingPageTypeMessage.loadSetting: - console.log('server setting', serverSetting); + console.log('server setting', arg); setServerSetting({ - PORT: arg.data.PORT, - GAUZY_API_SERVER_URL: arg.data.GAUZY_API_SERVER_URL, + PORT: arg.data.server.PORT, + GAUZY_API_SERVER_URL: arg.data.server.GAUZY_API_SERVER_URL, NEXT_PUBLIC_GAUZY_API_SERVER_URL: - arg.data.NEXT_PUBLIC_GAUZY_API_SERVER_URL, + arg.data.server.NEXT_PUBLIC_GAUZY_API_SERVER_URL, }); + setLng(arg.data.general.lang); break; case SettingPageTypeMessage.mainResponse: setPopupServer({ From 7fa5c33f70398baa701bba8eed14ae56aa074f3c Mon Sep 17 00:00:00 2001 From: sutralia Date: Sun, 23 Jun 2024 18:06:09 +0700 Subject: [PATCH 08/14] style: fix styling code --- apps/server-web/src/main/main.ts | 2 +- apps/server-web/src/renderer/components/General.tsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/server-web/src/main/main.ts b/apps/server-web/src/main/main.ts index b291b57ea..a8dfd1205 100644 --- a/apps/server-web/src/main/main.ts +++ b/apps/server-web/src/main/main.ts @@ -3,7 +3,7 @@ import { app, ipcMain, Tray, dialog, BrowserWindow, shell } from 'electron'; import { DesktopServer } from './helpers/desktop-server'; import { LocalStore } from './helpers/services/libs/desktop-store'; import { EventEmitter } from 'events'; -import { defaultTrayMenuItem, _initTray, updateTrayMenu, translateTrayMenu } from './tray'; +import { defaultTrayMenuItem, _initTray, updateTrayMenu } from './tray'; import { EventLists, SettingPageTypeMessage } from './helpers/constant'; import { resolveHtmlPath } from './util'; import Updater from './updater'; diff --git a/apps/server-web/src/renderer/components/General.tsx b/apps/server-web/src/renderer/components/General.tsx index 9330568c5..dc03bfe3b 100644 --- a/apps/server-web/src/renderer/components/General.tsx +++ b/apps/server-web/src/renderer/components/General.tsx @@ -34,9 +34,9 @@ export function GeneralComponent(props: Props) { fill="currentColor" > @@ -52,6 +52,7 @@ export function GeneralComponent(props: Props) { {props.langs.length > 0 && props.langs.map((lang) => ( Date: Sun, 23 Jun 2024 14:48:12 +0200 Subject: [PATCH 09/14] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0b7c7b902..94e3351f9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ever-teams", "homepage": "https://ever.team", - "license": "UNLICENSED", + "license": "AGPL-3.0", "author": { "name": "Ever Co. LTD", "email": "ever@ever.co", From de8ff9100b82309ab741e2a53dbdc113c0d71dbb Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Fri, 28 Jun 2024 05:31:02 +0200 Subject: [PATCH 10/14] feat: show notification when user is deleted to a team --- .../hooks/features/useOrganizationTeams.ts | 8 ++++- apps/web/app/stores/organization-team.ts | 10 +++++++ apps/web/components/ui/sonner.tsx | 29 +++++++++++++++++++ apps/web/components/ui/toaster.tsx | 26 +++++++++++++++++ apps/web/lib/layout/main-layout.tsx | 9 +++--- 5 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 apps/web/components/ui/sonner.tsx diff --git a/apps/web/app/hooks/features/useOrganizationTeams.ts b/apps/web/app/hooks/features/useOrganizationTeams.ts index fe9bc7406..8b4dfae1c 100644 --- a/apps/web/app/hooks/features/useOrganizationTeams.ts +++ b/apps/web/app/hooks/features/useOrganizationTeams.ts @@ -20,6 +20,7 @@ import { activeTeamIdState, activeTeamManagersState, activeTeamState, + isTeamMemberJustDeletedState, isTeamMemberState, organizationTeamsState, teamsFetchingState, @@ -175,6 +176,8 @@ export function useOrganizationTeams() { const [activeTeamId, setActiveTeamId] = useRecoilState(activeTeamIdState); const [teamsFetching, setTeamsFetching] = useRecoilState(teamsFetchingState); + const [isTeamMemberJustDeleted, setIsTeamMemberJustDeleted] = useRecoilState(isTeamMemberJustDeletedState); + // const [isTeamJustDeleted, setIsTeamJustDeleted] = useRecoilState(isTeamJustDeletedState); const { firstLoad, firstLoadData: firstLoadTeamsData } = useFirstLoad(); const [isTeamMember, setIsTeamMember] = useRecoilState(isTeamMemberState); const { updateUserFromAPI, refreshToken, user } = useAuthenticateUser(); @@ -245,6 +248,7 @@ export function useOrganizationTeams() { return queryCall(user?.employee.organizationId, user?.employee.tenantId).then((res) => { if (res.data?.items && res.data?.items?.length === 0) { setIsTeamMember(false); + setIsTeamMemberJustDeleted(true); } const latestTeams = res.data?.items || []; @@ -381,6 +385,8 @@ export function useOrganizationTeams() { removeUserFromAllTeam, loadingTeam, isTrackingEnabled, - memberActiveTaskId + memberActiveTaskId, + isTeamMemberJustDeleted, + setIsTeamMemberJustDeleted }; } diff --git a/apps/web/app/stores/organization-team.ts b/apps/web/app/stores/organization-team.ts index fa8bac0e5..da9bba708 100644 --- a/apps/web/app/stores/organization-team.ts +++ b/apps/web/app/stores/organization-team.ts @@ -21,6 +21,16 @@ export const isTeamMemberState = atom({ default: true }); +export const isTeamMemberJustDeletedState = atom({ + key: 'isTeamMemberJustDeletedState', + default: false +}); + +export const isTeamJustDeletedState = atom({ + key: 'isTeamJustDeletedState', + default: false +}); + export const isOTRefreshingState = atom({ key: 'isOTRefreshing', default: false diff --git a/apps/web/components/ui/sonner.tsx b/apps/web/components/ui/sonner.tsx new file mode 100644 index 000000000..1128edfce --- /dev/null +++ b/apps/web/components/ui/sonner.tsx @@ -0,0 +1,29 @@ +import { useTheme } from "next-themes" +import { Toaster as Sonner } from "sonner" + +type ToasterProps = React.ComponentProps + +const Toaster = ({ ...props }: ToasterProps) => { + const { theme = "system" } = useTheme() + + return ( + + ) +} + +export { Toaster } diff --git a/apps/web/components/ui/toaster.tsx b/apps/web/components/ui/toaster.tsx index a5b7b1090..f65577786 100644 --- a/apps/web/components/ui/toaster.tsx +++ b/apps/web/components/ui/toaster.tsx @@ -1,5 +1,10 @@ import { Toast, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport } from 'components/ui/toast'; import { useToast } from 'components/ui/use-toast'; +import { Toaster as ToasterMessage } from '@components/ui/sonner'; +import { toast } from 'sonner'; +import { useOrganizationTeams } from '@app/hooks'; +// import { useTranslations } from 'next-intl'; +import { useEffect, useState } from 'react'; export function Toaster() { const { toasts } = useToast(); @@ -22,3 +27,24 @@ export function Toaster() { ); } + +export function ToastMessageManager() { + const { isTeamMemberJustDeleted, setIsTeamMemberJustDeleted } = useOrganizationTeams(); + const [deletedNotifShowm, setDeletedNotifShowm] = useState(false); + // const t = useTranslations(); + + useEffect(() => { + let timer: NodeJS.Timeout; + if (isTeamMemberJustDeleted && !deletedNotifShowm) { + toast.error('You have been deleted from the team', { id: 'deletion-user', duration: 20000 }); + timer = setTimeout(() => { + setIsTeamMemberJustDeleted(false); + }, 10000); + setDeletedNotifShowm(true); + } + + return () => clearTimeout(timer); + }, [deletedNotifShowm, isTeamMemberJustDeleted, setIsTeamMemberJustDeleted]); + + return ; +} diff --git a/apps/web/lib/layout/main-layout.tsx b/apps/web/lib/layout/main-layout.tsx index 90ac1696e..6f5ec8c2f 100644 --- a/apps/web/lib/layout/main-layout.tsx +++ b/apps/web/lib/layout/main-layout.tsx @@ -1,7 +1,7 @@ 'use client'; import { clsxm } from '@app/utils'; -import { Toaster } from '@components/ui/toaster'; +import { Toaster, ToastMessageManager } from '@components/ui/toaster'; import { Container, Divider, Meta } from 'lib/components'; import { PropsWithChildren } from 'react'; import { Footer, Navbar } from '.'; @@ -41,14 +41,14 @@ export function MainLayout({ ? ` margin-left: 2rem; margin-right: 2rem; - ` : ` --tblr-gutter-x: 1.5rem; + ` + : ` --tblr-gutter-x: 1.5rem; --tblr-gutter-y: 0; width: 100%; padding-right: calc(var(--tblr-gutter-x) * 0.5); padding-left: calc(var(--tblr-gutter-x) * 0.5); margin-right: auto; - margin-left: auto;` - } + margin-left: auto;`} } `} @@ -77,6 +77,7 @@ export function MainLayout({
    +
    ); } From 16c7de578dfd94fde0b433b7a9f81d799977f5e5 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Fri, 28 Jun 2024 12:20:15 +0200 Subject: [PATCH 11/14] feat: user deleted switch to another team and show notification --- apps/web/app/hooks/features/useOrganizationTeams.ts | 1 + apps/web/components/ui/toaster.tsx | 6 ++++-- apps/web/locales/ar.json | 3 ++- apps/web/locales/bg.json | 3 ++- apps/web/locales/de.json | 3 ++- apps/web/locales/en.json | 3 ++- apps/web/locales/es.json | 3 ++- apps/web/locales/fr.json | 3 ++- apps/web/locales/he.json | 3 ++- apps/web/locales/it.json | 3 ++- apps/web/locales/nl.json | 3 ++- apps/web/locales/pl.json | 3 ++- apps/web/locales/pt.json | 3 ++- apps/web/locales/ru.json | 3 ++- apps/web/locales/zh.json | 3 ++- 15 files changed, 31 insertions(+), 15 deletions(-) diff --git a/apps/web/app/hooks/features/useOrganizationTeams.ts b/apps/web/app/hooks/features/useOrganizationTeams.ts index 8b4dfae1c..dadabf6aa 100644 --- a/apps/web/app/hooks/features/useOrganizationTeams.ts +++ b/apps/web/app/hooks/features/useOrganizationTeams.ts @@ -270,6 +270,7 @@ export function useOrganizationTeams() { // Handle case where user might Remove Account from all teams, // In such case need to update active team with Latest list of Teams if (!latestTeams.find((team: any) => team.id === teamId) && latestTeams.length) { + setIsTeamMemberJustDeleted(true); setActiveTeam(latestTeams[0]); } else if (!latestTeams.length) { teamId = ''; diff --git a/apps/web/components/ui/toaster.tsx b/apps/web/components/ui/toaster.tsx index f65577786..bf5b4ae54 100644 --- a/apps/web/components/ui/toaster.tsx +++ b/apps/web/components/ui/toaster.tsx @@ -5,6 +5,7 @@ import { toast } from 'sonner'; import { useOrganizationTeams } from '@app/hooks'; // import { useTranslations } from 'next-intl'; import { useEffect, useState } from 'react'; +import { useTranslations } from 'next-intl'; export function Toaster() { const { toasts } = useToast(); @@ -31,12 +32,12 @@ export function Toaster() { export function ToastMessageManager() { const { isTeamMemberJustDeleted, setIsTeamMemberJustDeleted } = useOrganizationTeams(); const [deletedNotifShowm, setDeletedNotifShowm] = useState(false); - // const t = useTranslations(); + const t = useTranslations(); useEffect(() => { let timer: NodeJS.Timeout; if (isTeamMemberJustDeleted && !deletedNotifShowm) { - toast.error('You have been deleted from the team', { id: 'deletion-user', duration: 20000 }); + toast.error(t('alerts.ALERT_USER_DELETED_FROM_TEAM'), { duration: 20000 }); timer = setTimeout(() => { setIsTeamMemberJustDeleted(false); }, 10000); @@ -44,6 +45,7 @@ export function ToastMessageManager() { } return () => clearTimeout(timer); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [deletedNotifShowm, isTeamMemberJustDeleted, setIsTeamMemberJustDeleted]); return ; diff --git a/apps/web/locales/ar.json b/apps/web/locales/ar.json index 6cb46bbf5..70fac3c32 100644 --- a/apps/web/locales/ar.json +++ b/apps/web/locales/ar.json @@ -212,7 +212,8 @@ "ALERT_ACCOUNT_PERMANENT_DELETE": "سيتم حذف حسابك نهائياً مع إزالته من جميع الفرق", "ALERT_REMOVE_TEAM": "سيتم إزالة الفريق بالكامل من النظام وفقدان أعضاء الفريق للوصول", "ALERT_REMOVE_ALL_DATA": "ستتم إزالة جميع بيانات الحساب من جميع الفرق التي تكون فيها مديرًا موجودًا واحدًا فقط", - "ALERT_QUIT_TEAM": "أنت على وشك مغادرة الفريق" + "ALERT_QUIT_TEAM": "أنت على وشك مغادرة الفريق", + "ALERT_USER_DELETED_FROM_TEAM": "لقد تم حذفك من الفريق" }, "pages": { "home": { diff --git a/apps/web/locales/bg.json b/apps/web/locales/bg.json index e4f3be4d2..d9bd9d69a 100644 --- a/apps/web/locales/bg.json +++ b/apps/web/locales/bg.json @@ -212,7 +212,8 @@ "ALERT_ACCOUNT_PERMANENT_DELETE": "Вашият профил ще бъде изтрит завинаги с премахване от всички отбори", "ALERT_REMOVE_TEAM": "Отборът ще бъде премахнат напълно от системата и членовете на отбора ще загубят достъп", "ALERT_REMOVE_ALL_DATA": "Всички данни за профила ще бъдат премахнати от всички отбори, освен където сте само мениджър", - "ALERT_QUIT_TEAM": "Ще напуснете отбора" + "ALERT_QUIT_TEAM": "Ще напуснете отбора", + "ALERT_USER_DELETED_FROM_TEAM": "Бяхте изтрит от екипа" }, "pages": { "home": { diff --git a/apps/web/locales/de.json b/apps/web/locales/de.json index 90d7c90e0..9f9d6c088 100644 --- a/apps/web/locales/de.json +++ b/apps/web/locales/de.json @@ -212,7 +212,8 @@ "ALERT_ACCOUNT_PERMANENT_DELETE": "Ihr Konto wird dauerhaft gelöscht und von allen Teams entfernt", "ALERT_REMOVE_TEAM": "Das Team wird vollständig aus dem System entfernt und die Teammitglieder verlieren den Zugriff", "ALERT_REMOVE_ALL_DATA": "Alle Kontodaten werden von allen Teams entfernt, in denen Sie der EINZIGE Manager sind", - "ALERT_QUIT_TEAM": "Sie sind im Begriff, das Team zu verlassen" + "ALERT_QUIT_TEAM": "Sie sind im Begriff, das Team zu verlassen", + "ALERT_USER_DELETED_FROM_TEAM": "Du wurdest aus dem Team entfernt" }, "pages": { "home": { diff --git a/apps/web/locales/en.json b/apps/web/locales/en.json index dd701989b..55c9c5bd1 100644 --- a/apps/web/locales/en.json +++ b/apps/web/locales/en.json @@ -212,7 +212,8 @@ "ALERT_ACCOUNT_PERMANENT_DELETE": "Your Account will be deleted permanently with removing from all teams", "ALERT_REMOVE_ALL_DATA": "All Account Data will be removed from all teams where you are ONLY one existed manager", "ALERT_REMOVE_TEAM": "Team will be completely removed for the system and team members lost access", - "ALERT_QUIT_TEAM": "You are about to quit the team" + "ALERT_QUIT_TEAM": "You are about to quit the team", + "ALERT_USER_DELETED_FROM_TEAM": "You have been deleted from the team" }, "pages": { "home": { diff --git a/apps/web/locales/es.json b/apps/web/locales/es.json index f5af900c8..93ac932f9 100644 --- a/apps/web/locales/es.json +++ b/apps/web/locales/es.json @@ -212,7 +212,8 @@ "ALERT_ACCOUNT_PERMANENT_DELETE": "Tu cuenta será eliminada permanentemente con la eliminación de todos los equipos", "ALERT_REMOVE_TEAM": "El equipo será eliminado completamente del sistema y los miembros del equipo perderán el acceso", "ALERT_REMOVE_ALL_DATA": "Todos los datos de la cuenta serán eliminados de todos los equipos donde eres el único administrador existente", - "ALERT_QUIT_TEAM": "Estás a punto de abandonar el equipo" + "ALERT_QUIT_TEAM": "Estás a punto de abandonar el equipo", + "ALERT_USER_DELETED_FROM_TEAM": "Has sido eliminado del equipo" }, "pages": { "home": { diff --git a/apps/web/locales/fr.json b/apps/web/locales/fr.json index 0699a5e3c..4c36ec885 100644 --- a/apps/web/locales/fr.json +++ b/apps/web/locales/fr.json @@ -212,7 +212,8 @@ "ALERT_ACCOUNT_PERMANENT_DELETE": "Votre compte sera supprimé définitivement avec suppression de toutes les équipes", "ALERT_REMOVE_TEAM": "L'équipe sera complètement supprimée du système et les membres de l'équipe perdront l'accès", "ALERT_REMOVE_ALL_DATA": "Toutes les données de compte seront supprimées de toutes les équipes où vous êtes UNIQUEMENT un manager existant", - "ALERT_QUIT_TEAM": "Vous êtes sur le point de quitter l'équipe" + "ALERT_QUIT_TEAM": "Vous êtes sur le point de quitter l'équipe", + "ALERT_USER_DELETED_FROM_TEAM": "Vous avez été supprimé de l'équipe" }, "pages": { "home": { diff --git a/apps/web/locales/he.json b/apps/web/locales/he.json index 57b4adb9d..4760f8573 100644 --- a/apps/web/locales/he.json +++ b/apps/web/locales/he.json @@ -212,7 +212,8 @@ "ALERT_ACCOUNT_PERMANENT_DELETE": "החשבון שלך יימחק לצמיתות עם הסרה מכל הצוותים", "ALERT_REMOVE_ALL_DATA": "כל נתוני החשבון יוסרו מכל הצוותים שבהם אתה רק מנהל קיים אחד", "ALERT_REMOVE_TEAM": "הצוות יוסר לחלוטין מהמערכת וחברי הצוות יאבדו גישה", - "ALERT_QUIT_TEAM": "אתה עומד לעזוב את הצוות" + "ALERT_QUIT_TEAM": "אתה עומד לעזוב את הצוות", + "ALERT_USER_DELETED_FROM_TEAM": "נמחקת מהקבוצה" }, "pages": { "home": { diff --git a/apps/web/locales/it.json b/apps/web/locales/it.json index 7f3a694bc..437a409f5 100644 --- a/apps/web/locales/it.json +++ b/apps/web/locales/it.json @@ -212,7 +212,8 @@ "ALERT_ACCOUNT_PERMANENT_DELETE": "Il tuo account verrà eliminato definitivamente e sarai rimosso da tutti i team", "ALERT_REMOVE_ALL_DATA": "Tutti i dati dell'account saranno rimossi dai team in cui sei l'unico responsabile esistente", "ALERT_REMOVE_TEAM": "Il Team sarà completamente rimosso dal sistema e i membri del team perderanno l'accesso", - "ALERT_QUIT_TEAM": "Stai per abbandonare il team" + "ALERT_QUIT_TEAM": "Stai per abbandonare il team", + "ALERT_USER_DELETED_FROM_TEAM": "Sei stato rimosso dal team" }, "pages": { "home": { diff --git a/apps/web/locales/nl.json b/apps/web/locales/nl.json index 7edc65081..dd88cca6f 100644 --- a/apps/web/locales/nl.json +++ b/apps/web/locales/nl.json @@ -212,7 +212,8 @@ "ALERT_ACCOUNT_PERMANENT_DELETE": "Uw account wordt permanent verwijderd en uit alle teams verwijderd", "ALERT_REMOVE_ALL_DATA": "Alle accountgegevens worden verwijderd uit alle teams waar je ALLEEN een bestaande manager bent.", "ALERT_REMOVE_TEAM": "Team wordt volledig verwijderd uit het systeem en teamleden verliezen toegang", - "ALERT_QUIT_TEAM": "U staat op het punt het team te verlaten" + "ALERT_QUIT_TEAM": "U staat op het punt het team te verlaten", + "ALERT_USER_DELETED_FROM_TEAM": "Je bent uit het team verwijderd" }, "pages": { "home": { diff --git a/apps/web/locales/pl.json b/apps/web/locales/pl.json index 52ffa643c..7a4ac5348 100644 --- a/apps/web/locales/pl.json +++ b/apps/web/locales/pl.json @@ -212,7 +212,8 @@ "ALERT_ACCOUNT_PERMANENT_DELETE": "Twoje Konto zostanie trwale usunięte wraz z usunięciem z wszystkich zespołów", "ALERT_REMOVE_ALL_DATA": "Wszystkie dane konta zostaną usunięte z zespołów, w których jesteś jedynym istniejącym menedżerem", "ALERT_REMOVE_TEAM": "Zespół zostanie całkowicie usunięty z systemu, a członkowie zespołu stracą dostęp", - "ALERT_QUIT_TEAM": "Zamierzasz opuścić zespół" + "ALERT_QUIT_TEAM": "Zamierzasz opuścić zespół", + "ALERT_USER_DELETED_FROM_TEAM": "Zostałeś usunięty z zespołu" }, "pages": { "home": { diff --git a/apps/web/locales/pt.json b/apps/web/locales/pt.json index 17b6d4537..eeb4c8125 100644 --- a/apps/web/locales/pt.json +++ b/apps/web/locales/pt.json @@ -212,7 +212,8 @@ "ALERT_ACCOUNT_PERMANENT_DELETE": "Sua conta será excluída permanentemente com a remoção de todas as equipes", "ALERT_REMOVE_TEAM": "A equipe será completamente removida do sistema e os membros da equipe perderão o acesso", "ALERT_REMOVE_ALL_DATA": "Todos os dados da conta serão removidos de todas as equipes onde você é o ÚNICO gerente existente", - "ALERT_QUIT_TEAM": "Você está prestes a sair da equipe" + "ALERT_QUIT_TEAM": "Você está prestes a sair da equipe", + "ALERT_USER_DELETED_FROM_TEAM": "Você foi removido da equipe" }, "pages": { "home": { diff --git a/apps/web/locales/ru.json b/apps/web/locales/ru.json index 79b18e7f9..580ff8755 100644 --- a/apps/web/locales/ru.json +++ b/apps/web/locales/ru.json @@ -212,7 +212,8 @@ "ALERT_ACCOUNT_PERMANENT_DELETE": "Ваш аккаунт будет удален окончательно с удалением из всех команд", "ALERT_REMOVE_TEAM": "Команда будет полностью удалена из системы, и участники команды потеряют доступ", "ALERT_REMOVE_ALL_DATA": "Все данные аккаунта будут удалены из всех команд, где вы единственный существующий управляющий", - "ALERT_QUIT_TEAM": "Вы собираетесь покинуть команду" + "ALERT_QUIT_TEAM": "Вы собираетесь покинуть команду", + "ALERT_USER_DELETED_FROM_TEAM": "Вы были удалены из команды" }, "pages": { "home": { diff --git a/apps/web/locales/zh.json b/apps/web/locales/zh.json index 3431ba67c..6062a5093 100644 --- a/apps/web/locales/zh.json +++ b/apps/web/locales/zh.json @@ -212,7 +212,8 @@ "ALERT_ACCOUNT_PERMANENT_DELETE": "您的账户将被永久删除,并从所有团队中移除", "ALERT_REMOVE_TEAM": "团队将被完全移除出系统,成员也将失去访问权限", "ALERT_REMOVE_ALL_DATA": "所有帐户数据将从您仅是一名现有经理的所有团队中删除", - "ALERT_QUIT_TEAM": "您将要退出团队" + "ALERT_QUIT_TEAM": "您将要退出团队", + "ALERT_USER_DELETED_FROM_TEAM": "您已从团队中删除" }, "pages": { "home": { From e7ba658647d684231005eddc30238a3f9c77ebd0 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Fri, 28 Jun 2024 12:56:07 +0200 Subject: [PATCH 12/14] fix: missing dependencies --- .cspell.json | 3 +++ apps/web/components/ui/toaster.tsx | 8 ++++---- apps/web/package.json | 1 + yarn.lock | 5 +++++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.cspell.json b/.cspell.json index b3dc78e37..a1cef8232 100644 --- a/.cspell.json +++ b/.cspell.json @@ -259,6 +259,9 @@ "skey", "smalltext", "snyk", + "Sonner", + "sonner", + "Notif", "stackoverflow", "statsus", "statut", diff --git a/apps/web/components/ui/toaster.tsx b/apps/web/components/ui/toaster.tsx index bf5b4ae54..b539b6f6c 100644 --- a/apps/web/components/ui/toaster.tsx +++ b/apps/web/components/ui/toaster.tsx @@ -31,22 +31,22 @@ export function Toaster() { export function ToastMessageManager() { const { isTeamMemberJustDeleted, setIsTeamMemberJustDeleted } = useOrganizationTeams(); - const [deletedNotifShowm, setDeletedNotifShowm] = useState(false); + const [deletedNotifShown, setDeletedNotifShown] = useState(false); const t = useTranslations(); useEffect(() => { let timer: NodeJS.Timeout; - if (isTeamMemberJustDeleted && !deletedNotifShowm) { + if (isTeamMemberJustDeleted && !deletedNotifShown) { toast.error(t('alerts.ALERT_USER_DELETED_FROM_TEAM'), { duration: 20000 }); timer = setTimeout(() => { setIsTeamMemberJustDeleted(false); }, 10000); - setDeletedNotifShowm(true); + setDeletedNotifShown(true); } return () => clearTimeout(timer); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [deletedNotifShowm, isTeamMemberJustDeleted, setIsTeamMemberJustDeleted]); + }, [deletedNotifShown, isTeamMemberJustDeleted, setIsTeamMemberJustDeleted]); return ; } diff --git a/apps/web/package.json b/apps/web/package.json index f148d12da..8de65d01e 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -101,6 +101,7 @@ "slate-hyperscript": "^0.77.0", "slate-react": "^0.91.0", "slate-serializers": "^0.4.0", + "sonner": "^1.5.0", "string-to-color": "^2.2.2", "tailwind-merge": "^1.14.0", "tailwindcss": "^3.1.8", diff --git a/yarn.lock b/yarn.lock index 3745a1c1d..dff420565 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23095,6 +23095,11 @@ socks@^2.7.1: ip-address "^9.0.5" smart-buffer "^4.2.0" +sonner@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/sonner/-/sonner-1.5.0.tgz#af359f817063318415326b33aab54c5d17c747b7" + integrity sha512-FBjhG/gnnbN6FY0jaNnqZOMmB73R+5IiyYAw8yBj7L54ER7HB3fOSE5OFiQiE2iXWxeXKvg6fIP4LtVppHEdJA== + sort-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" From edc6970d7775df85bda8816f55873df0ff3cbcaf Mon Sep 17 00:00:00 2001 From: "Gloire Mutaliko (Salva)" <86450367+GloireMutaliko21@users.noreply.github.com> Date: Fri, 28 Jun 2024 23:26:26 +0200 Subject: [PATCH 13/14] feat: resize tasks and members panels (#2650) --- apps/web/app/[locale]/page-component.tsx | 64 +++++---- .../app/[locale]/profile/[memberId]/page.tsx | 126 ++++++++++-------- apps/web/components/ui/resizable.tsx | 43 ++++++ apps/web/package.json | 1 + apps/web/styles/globals.css | 14 ++ yarn.lock | 5 + 6 files changed, 173 insertions(+), 80 deletions(-) create mode 100644 apps/web/components/ui/resizable.tsx diff --git a/apps/web/app/[locale]/page-component.tsx b/apps/web/app/[locale]/page-component.tsx index df67c6220..ffd828bf6 100644 --- a/apps/web/app/[locale]/page-component.tsx +++ b/apps/web/app/[locale]/page-component.tsx @@ -29,6 +29,7 @@ import { headerTabs } from '@app/stores/header-tabs'; import { usePathname } from 'next/navigation'; import { PeoplesIcon } from 'assets/svg'; import TeamMemberHeader from 'lib/features/team-member-header'; +import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@components/ui/resizable'; function MainPage() { const t = useTranslations(); @@ -63,31 +64,48 @@ function MainPage() {
    -
    - {/* */} -
    -
    -
    -
    - - +
    + + {/* */} + +
    +
    +
    +
    + + +
    +
    + +
    +
    +
    + + + {isTeamMember ? ( + + ) : null} +
    +
    -
    - -
    -
    -
    - - - {isTeamMember ? ( - - ) : null}
    - -
    -
    - {/* */} - {isTeamMember ? : } + + + + {/* */} + +
    {isTeamMember ? : }
    +
    +
    diff --git a/apps/web/app/[locale]/profile/[memberId]/page.tsx b/apps/web/app/[locale]/profile/[memberId]/page.tsx index d662d217c..6a500ece2 100644 --- a/apps/web/app/[locale]/profile/[memberId]/page.tsx +++ b/apps/web/app/[locale]/profile/[memberId]/page.tsx @@ -22,6 +22,7 @@ import { ScreenshootTab } from 'lib/features/activity/screenshoots'; import { AppsTab } from 'lib/features/activity/apps'; import { VisitedSitesTab } from 'lib/features/activity/visited-sites'; import { activityTypeState } from '@app/stores/activity-type'; +import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@components/ui/resizable'; export type FilterTab = 'Tasks' | 'Screenshots' | 'Apps' | 'Visited Sites'; @@ -100,66 +101,77 @@ const Profile = React.memo(function ProfilePage({ params }: { params: { memberId ) : ( - - {/* Breadcrumb */} -
    - - - - - -
    - - {/* User Profile Detail */} -
    - - - {profileIsAuthUser && isTrackingEnabled && ( - - )} -
    - {/* TaskFilter */} - -
    - {/* Divider */} -
    - {hook.tab == 'worked' && canSeeActivity && ( - -
    - {Object.keys(activityScreens).map((filter, i) => ( -
    - {i !== 0 && } -
    + + + {/* Breadcrumb */} +
    + + + + + +
    + + {/* User Profile Detail */} +
    + + + {profileIsAuthUser && isTrackingEnabled && ( + changeActivityFilter(filter as FilterTab)} - > - {filter} -
    + /> + )} +
    + {/* TaskFilter */} + + + + + + {/* Divider */} + {/*
    */} + + {hook.tab == 'worked' && canSeeActivity && ( + +
    + {Object.keys(activityScreens).map((filter, i) => ( +
    + {i !== 0 && } +
    changeActivityFilter(filter as FilterTab)} + > + {filter} +
    +
    + ))}
    - ))} -
    - - )} - - - {hook.tab !== 'worked' || activityFilter == 'Tasks' ? ( - - ) : ( - activityScreens[activityFilter] ?? null - )} - + + )} + + + {hook.tab !== 'worked' || activityFilter == 'Tasks' ? ( + + ) : ( + activityScreens[activityFilter] ?? null + )} + + + )} diff --git a/apps/web/components/ui/resizable.tsx b/apps/web/components/ui/resizable.tsx new file mode 100644 index 000000000..df9bbf8f5 --- /dev/null +++ b/apps/web/components/ui/resizable.tsx @@ -0,0 +1,43 @@ +import { GripVertical } from "lucide-react" +import * as ResizablePrimitive from "react-resizable-panels" + +import { cn } from "lib/utils" + +const ResizablePanelGroup = ({ + className, + ...props +}: React.ComponentProps) => ( + +) + +const ResizablePanel = ResizablePrimitive.Panel + +const ResizableHandle = ({ + withHandle, + className, + ...props +}: React.ComponentProps & { + withHandle?: boolean +}) => ( + div]:rotate-90", + className + )} + {...props} + > + {withHandle && ( +
    + +
    + )} +
    +) + +export { ResizablePanelGroup, ResizablePanel, ResizableHandle } diff --git a/apps/web/package.json b/apps/web/package.json index 8de65d01e..26e7904d1 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -94,6 +94,7 @@ "react-paginate": "^8.2.0", "react-popper": "^2.3.0", "react-popper-tooltip": "^4.4.2", + "react-resizable-panels": "^2.0.19", "recoil": "^0.7.7", "sharp": "^0.32.6", "slate": "^0.90.0", diff --git a/apps/web/styles/globals.css b/apps/web/styles/globals.css index 44d638c3a..f9c16d99b 100644 --- a/apps/web/styles/globals.css +++ b/apps/web/styles/globals.css @@ -186,6 +186,20 @@ html.dark { scrollbar-width: none; /* Firefox */ } + .custom-scrollbar::-webkit-scrollbar { + display: block; + width: 8px; + height: 5px; + @apply dark:bg-[#7b7b7c] bg-gray-400; + } + .custom-scrollbar:hover::-webkit-scrollbar { + padding: 1px; + } + + .custom-scrollbar::-webkit-scrollbar-thumb { + @apply dark:bg-[#484848] bg-gray-300; + } + /* Details Aside Bar */ .details-label { diff --git a/yarn.lock b/yarn.lock index dff420565..d6a9c2c9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21711,6 +21711,11 @@ react-remove-scroll@2.5.5: 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" + integrity sha512-v3E41kfKSuCPIvJVb4nL4mIZjjKIn/gh6YqZF/gDfQDolv/8XnhJBek4EiV2gOr3hhc5A3kOGOayk3DhanpaQw== + react-router-dom@^6.16.0: version "6.23.1" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.23.1.tgz#30cbf266669693e9492aa4fc0dde2541ab02322f" From 6e80735e00ee5895238f9b0c3609ef414dea4693 Mon Sep 17 00:00:00 2001 From: Ruslan Konviser Date: Sat, 29 Jun 2024 14:14:11 +0200 Subject: [PATCH 14/14] chore: add Web Server build and other improvements --- .czrc | 3 + .gitattributes | 15 + .github/workflows/deploy-vercel-dev.yml | 2 +- .github/workflows/deploy-vercel-prod.yml | 2 +- .github/workflows/deploy-vercel-stage.yml | 2 +- .github/workflows/desktop-server-api.apps.yml | 10 +- .github/workflows/desktop-server-web.apps.yml | 335 ++++++++++++++++++ .github/workflows/desktop.apps.yml | 10 +- .github/workflows/extensions.dev.yml | 2 +- .github/workflows/extensions.prod.yml | 2 +- .github/workflows/mobile.apps.android.yml | 2 +- .github/workflows/mobile.apps.ios.yml | 2 +- .github/workflows/mobile.before-merge.yml | 2 +- .github/workflows/mobile.dev.yml | 2 +- .github/workflows/mobile.prod.yml | 2 +- .github/workflows/web.before-merge.yml | 2 +- .gitignore | 21 ++ .prettierrc | 1 + .scripts/bump-version-electron.js | 113 ++++++ .snyk | 5 + SECURITY.md | 11 + apps/extensions/package.json | 2 +- apps/mobile/package.json | 2 +- apps/server-web/package.json | 2 +- apps/web/package.json | 13 +- crowdin.yml | 0 dc.cmd | 3 + nx.json | 6 +- package.json | 59 ++- yarn.lock | 23 +- 30 files changed, 616 insertions(+), 40 deletions(-) create mode 100644 .czrc create mode 100644 .gitattributes create mode 100644 .github/workflows/desktop-server-web.apps.yml create mode 100644 .scripts/bump-version-electron.js create mode 100644 .snyk create mode 100644 SECURITY.md create mode 100644 crowdin.yml create mode 100644 dc.cmd diff --git a/.czrc b/.czrc new file mode 100644 index 000000000..d1bcc209c --- /dev/null +++ b/.czrc @@ -0,0 +1,3 @@ +{ + "path": "cz-conventional-changelog" +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..64f740bce --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +LICENSE text +Jenkinfile text + +*.conf text +*.js text +*.ts text +*.json text +*.md text +*.sample text +*.txt text +*.yaml text +*.yml text + +*.jar binary +*.png binary diff --git a/.github/workflows/deploy-vercel-dev.yml b/.github/workflows/deploy-vercel-dev.yml index 00d1938e3..16631fc60 100644 --- a/.github/workflows/deploy-vercel-dev.yml +++ b/.github/workflows/deploy-vercel-dev.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v4 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.11.1' cache: 'yarn' diff --git a/.github/workflows/deploy-vercel-prod.yml b/.github/workflows/deploy-vercel-prod.yml index 5343b3193..e5c706a3c 100644 --- a/.github/workflows/deploy-vercel-prod.yml +++ b/.github/workflows/deploy-vercel-prod.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v4 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.11.1' cache: 'yarn' diff --git a/.github/workflows/deploy-vercel-stage.yml b/.github/workflows/deploy-vercel-stage.yml index 920259c91..18fb2546a 100644 --- a/.github/workflows/deploy-vercel-stage.yml +++ b/.github/workflows/deploy-vercel-stage.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v4 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.11.1' cache: 'yarn' diff --git a/.github/workflows/desktop-server-api.apps.yml b/.github/workflows/desktop-server-api.apps.yml index aa0bdbd51..110366428 100644 --- a/.github/workflows/desktop-server-api.apps.yml +++ b/.github/workflows/desktop-server-api.apps.yml @@ -54,7 +54,7 @@ jobs: run: 'yarn bootstrap' - name: Bump version - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const script = require('./.scripts/bump-version-electron.js') @@ -111,7 +111,7 @@ jobs: ref: master - name: Install Node.js, NPM and Yarn - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.11.1' cache: 'yarn' @@ -132,7 +132,7 @@ jobs: run: 'yarn bootstrap' - name: Bump version - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const script = require('./.scripts/bump-version-electron.js') @@ -189,7 +189,7 @@ jobs: ref: master - name: Install Node.js, NPM and Yarn - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.11.1' cache: 'yarn' @@ -210,7 +210,7 @@ jobs: run: 'yarn bootstrap' - name: Bump version - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const script = require('./.scripts/bump-version-electron.js') diff --git a/.github/workflows/desktop-server-web.apps.yml b/.github/workflows/desktop-server-web.apps.yml new file mode 100644 index 000000000..bc27569ec --- /dev/null +++ b/.github/workflows/desktop-server-web.apps.yml @@ -0,0 +1,335 @@ +name: Server Web Build Apps + +on: + workflow_run: + workflows: ['Release Apps'] + branches: [apps] + types: + - completed + +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +jobs: + release-linux: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [buildjet-8vcpu-ubuntu-2204] + + steps: + - name: Check out Git repository + uses: actions/checkout@v4 + + - name: Install Node.js, NPM and Yarn + uses: buildjet/setup-node@v4 + with: + node-version: '20.11.1' + cache: 'yarn' + + - name: Change permissions + run: 'sudo chown -R $(whoami) ./*' + + - name: Install system dependencies + run: 'sudo apt-get update && sudo apt install -y curl gnupg git libappindicator3-1 ca-certificates binutils icnsutils graphicsmagick' + + - name: Fix node-gyp and Python + run: python3 -m pip install packaging setuptools + + - name: Install latest version of NPM + run: 'sudo npm install -g npm@9' + + - name: Install latest node-gyp package + run: 'sudo npm install --quiet -g node-gyp@9.3.1' + + - name: Install Yarn dependencies + run: 'yarn install --network-timeout 1000000 --frozen-lockfile' + + - name: Bootstrap Yarn + run: 'yarn bootstrap' + + - name: Bump version + uses: actions/github-script@v7 + with: + script: | + const script = require('./.scripts/bump-version-electron.js') + console.log(script.serverweb(true)) + env: + PROJECT_REPO: 'https://github.com/ever-co/ever-teams.git' + DESKTOP_WEB_SERVER_APP_NAME: 'ever-teams-web-server' + COMPANY_SITE_LINK: 'https://ever.team' + DESKTOP_WEB_SERVER_APP_DESCRIPTION: 'Ever Teams Web Server' + DESKTOP_WEB_SERVER_APP_ID: 'com.ever.everteamswebserver' + + - name: Build Web Server App + run: 'yarn build:web-server:linux:release:gh' + env: + USE_HARD_LINKS: false + GH_TOKEN: ${{ secrets.GH_TOKEN }} + EP_GH_IGNORE_TIME: true + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + SENTRY_TRACES_SAMPLE_RATE: '${{ secrets.SENTRY_TRACES_SAMPLE_RATE }}' + SENTRY_HTTP_TRACING_ENABLED: '${{ secrets.SENTRY_HTTP_TRACING_ENABLED }}' + SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' + DO_KEY_ID: ${{ secrets.DO_KEY_ID }} + DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true + COMPANY_SITE: 'Ever Teams' + COMPANY_SITE_LINK: 'https://ever.team' + COMPANY_FACEBOOK_LINK: 'https://www.facebook.com/everteamshq' + COMPANY_TWITTER_LINK: 'https://twitter.com/ever_teams' + COMPANY_LINKEDIN_LINK: 'https://www.linkedin.com/company/ever-co' + PROJECT_REPO: 'https://github.com/ever-co/ever-teams.git' + DESKTOP_WEB_SERVER_APP_NAME: 'ever-teams-web-server' + DESKTOP_WEB_SERVER_APP_DESCRIPTION: 'Ever Teams Web Server' + DESKTOP_WEB_SERVER_APP_ID: 'com.ever.everteamswebserver' + DESKTOP_WEB_SERVER_APP_REPO_NAME: 'ever-teams-web-server' + DESKTOP_WEB_SERVER_APP_REPO_OWNER: 'ever-co' + DESKTOP_WEB_SERVER_APP_WELCOME_TITLE: 'Welcome to Ever Teams' + DESKTOP_WEB_SERVER_APP_WELCOME_CONTENT: 'Ever Teams is a productivity tool that helps you to stay focused on your work and manage your team work better.' + I18N_FILES_URL: 'https://raw.githubusercontent.com/ever-co/ever-teams/develop/apps/server-web/i18n' + PLATFORM_LOGO: 'https://app.ever.team/assets/ever-teams.png' + GAUZY_DESKTOP_LOGO_512X512: 'https://raw.githubusercontent.com/ever-co/ever-teams/develop/apps/server-web/src/assets/icons/icon_512x512.png' + + release-mac: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [macos-12] + + steps: + - name: Check out Git repository + uses: actions/checkout@v4 + + - name: Install Node.js, NPM and Yarn + uses: actions/setup-node@v4 + with: + node-version: '20.11.1' + cache: 'yarn' + + - name: Fix node-gyp and Python + run: python3 -m pip install packaging setuptools + + - name: Install latest version of NPM + run: 'sudo npm install -g npm@9' + + - name: Install latest node-gyp package + run: 'sudo npm install --quiet -g node-gyp@9.3.1' + + - name: Install Yarn dependencies + run: 'yarn install --network-timeout 1000000 --frozen-lockfile' + + - name: Bootstrap Yarn + run: 'yarn bootstrap' + + - name: Bump version + uses: actions/github-script@v7 + with: + script: | + const script = require('./.scripts/bump-version-electron.js') + console.log(script.serverweb(true)) + env: + PROJECT_REPO: 'https://github.com/ever-co/ever-teams.git' + DESKTOP_WEB_SERVER_APP_NAME: 'ever-teams-web-server' + COMPANY_SITE_LINK: 'https://ever.team' + DESKTOP_WEB_SERVER_APP_DESCRIPTION: 'Ever Teams Web Server' + DESKTOP_WEB_SERVER_APP_ID: 'com.ever.everteamswebserver' + + - name: Build Web Server App + run: 'yarn build:web-server:mac:release' + env: + USE_HARD_LINKS: false + GH_TOKEN: ${{ secrets.GH_TOKEN }} + EP_GH_IGNORE_TIME: true + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + SENTRY_TRACES_SAMPLE_RATE: '${{ secrets.SENTRY_TRACES_SAMPLE_RATE }}' + SENTRY_HTTP_TRACING_ENABLED: '${{ secrets.SENTRY_HTTP_TRACING_ENABLED }}' + SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' + DO_KEY_ID: ${{ secrets.DO_KEY_ID }} + DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true + COMPANY_SITE: 'Ever Teams' + COMPANY_SITE_LINK: 'https://ever.team' + COMPANY_FACEBOOK_LINK: 'https://www.facebook.com/everteamshq' + COMPANY_TWITTER_LINK: 'https://twitter.com/ever_teams' + COMPANY_LINKEDIN_LINK: 'https://www.linkedin.com/company/ever-co' + PROJECT_REPO: 'https://github.com/ever-co/ever-teams.git' + DESKTOP_WEB_SERVER_APP_NAME: 'ever-teams-web-server' + DESKTOP_WEB_SERVER_APP_DESCRIPTION: 'Ever Teams Web Server' + DESKTOP_WEB_SERVER_APP_ID: 'com.ever.everteamswebserver' + DESKTOP_WEB_SERVER_APP_REPO_NAME: 'ever-teams-web-server' + DESKTOP_WEB_SERVER_APP_REPO_OWNER: 'ever-co' + DESKTOP_WEB_SERVER_APP_WELCOME_TITLE: 'Welcome to Ever Teams' + DESKTOP_WEB_SERVER_APP_WELCOME_CONTENT: 'Ever Teams is a productivity tool that helps you to stay focused on your work and manage your team work better.' + I18N_FILES_URL: 'https://raw.githubusercontent.com/ever-co/ever-teams/develop/apps/server-web/i18n' + PLATFORM_LOGO: 'https://app.ever.team/assets/ever-teams.png' + GAUZY_DESKTOP_LOGO_512X512: 'https://raw.githubusercontent.com/ever-co/ever-teams/develop/apps/server-web/src/assets/icons/icon_512x512.png' + + release-windows: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [windows-latest] + + steps: + - name: Check out Git repository + uses: actions/checkout@v4 + + - name: Install Node.js, NPM and Yarn + uses: actions/setup-node@v4 + with: + node-version: '20.11.1' + cache: 'yarn' + + - name: Fix node-gyp and Python + run: python3 -m pip install packaging setuptools + + - name: Install latest version of NPM + run: 'npm install -g npm@9' + + - name: Install latest node-gyp package + run: 'npm install --quiet -g node-gyp@9.3.1' + + - name: Install Yarn dependencies + run: 'yarn install --network-timeout 1000000 --frozen-lockfile' + + - name: Bootstrap Yarn + run: 'yarn bootstrap' + + - name: Bump version + uses: actions/github-script@v7 + with: + script: | + const script = require('./.scripts/bump-version-electron.js') + console.log(script.serverweb(true)) + env: + PROJECT_REPO: 'https://github.com/ever-co/ever-teams.git' + DESKTOP_WEB_SERVER_APP_NAME: 'ever-teams-web-server' + COMPANY_SITE_LINK: 'https://ever.team' + DESKTOP_WEB_SERVER_APP_DESCRIPTION: 'Ever Teams Web Server' + DESKTOP_WEB_SERVER_APP_ID: 'com.ever.everteamswebserver' + + - name: Print environment variable names + run: | + echo "Environment Variable Names:" + printenv | cut -d= -f1 + + - name: Build Web Server App + run: 'yarn build:web-server:windows:release:gh' + env: + USE_HARD_LINKS: false + GH_TOKEN: ${{ secrets.GH_TOKEN }} + EP_GH_IGNORE_TIME: true + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + SENTRY_TRACES_SAMPLE_RATE: '${{ secrets.SENTRY_TRACES_SAMPLE_RATE }}' + SENTRY_HTTP_TRACING_ENABLED: '${{ secrets.SENTRY_HTTP_TRACING_ENABLED }}' + SENTRY_POSTGRES_TRACKING_ENABLED: '${{ secrets.SENTRY_POSTGRES_TRACKING_ENABLED }}' + DO_KEY_ID: ${{ secrets.DO_KEY_ID }} + DO_SECRET_KEY: ${{ secrets.DO_SECRET_KEY }} + NX_NO_CLOUD: true + COMPANY_SITE: 'Ever Teams' + COMPANY_SITE_LINK: 'https://ever.team' + COMPANY_FACEBOOK_LINK: 'https://www.facebook.com/everteamshq' + COMPANY_TWITTER_LINK: 'https://twitter.com/ever_teams' + COMPANY_LINKEDIN_LINK: 'https://www.linkedin.com/company/ever-co' + PROJECT_REPO: 'https://github.com/ever-co/ever-teams.git' + DESKTOP_WEB_SERVER_APP_NAME: 'ever-teams-web-server' + DESKTOP_WEB_SERVER_APP_DESCRIPTION: 'Ever Teams Web Server' + DESKTOP_WEB_SERVER_APP_ID: 'com.ever.everteamswebserver' + DESKTOP_WEB_SERVER_APP_REPO_NAME: 'ever-teams-web-server' + DESKTOP_WEB_SERVER_APP_REPO_OWNER: 'ever-co' + DESKTOP_WEB_SERVER_APP_WELCOME_TITLE: 'Welcome to Ever Teams' + DESKTOP_WEB_SERVER_APP_WELCOME_CONTENT: 'Ever Teams is a productivity tool that helps you to stay focused on your work and manage your team work better.' + I18N_FILES_URL: 'https://raw.githubusercontent.com/ever-co/ever-teams/develop/apps/server-web/i18n' + PLATFORM_LOGO: 'https://app.ever.team/assets/ever-teams.png' + GAUZY_DESKTOP_LOGO_512X512: 'https://raw.githubusercontent.com/ever-co/ever-teams/develop/apps/server-web/src/assets/icons/icon_512x512.png' + # Override unwanted environment variables + ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE: '' + ANT_HOME: '' + AZURE_CONFIG_DIR: '' + AZURE_DEVOPS_CACHE_DIR: '' + AZURE_EXTENSION_DIR: '' + AZ_DEVOPS_GLOBAL_CONFIG_DIR: '' + CABAL_DIR: '' + ChocolateyInstall: '' + ChromeWebDriver: '' + COBERTURA_HOME: '' + SBT_HOME: '' + SELENIUM_JAR_PATH: '' + STATS_BLT: '' + STATS_D: '' + STATS_D_D: '' + STATS_EXT: '' + STATS_EXTP: '' + STATS_RDCL: '' + STATS_TIS: '' + STATS_TRP: '' + STATS_UE: '' + STATS_V3PS: '' + STATS_VMD: '' + STATS_VMFE: '' + ANDROID_HOME: '' + ANDROID_NDK: '' + ANDROID_NDK_HOME: '' + ANDROID_NDK_LATEST_HOME: '' + ANDROID_NDK_ROOT: '' + ANDROID_SDK_ROOT: '' + GOROOT_1_20_X64: '' + GOROOT_1_21_X64: '' + GOROOT_1_22_X64: '' + GRADLE_HOME: '' + IEWebDriver: '' + ImageOS: '' + ImageVersion: '' + JAVA_HOME: '' + JAVA_HOME_11_X64: '' + JAVA_HOME_17_X64: '' + JAVA_HOME_21_X64: '' + JAVA_HOME_8_X64: '' + M2: '' + M2_REPO: '' + MAVEN_OPTS: '' + MonAgentClientLocation: '' + PGBIN: '' + PGDATA: '' + PGPASSWORD: '' + PGROOT: '' + PGUSER: '' + PHPROOT: '' + PIPX_BIN_DIR: '' + PIPX_HOME: '' + POWERSHELL_DISTRIBUTION_CHANNEL: '' + POWERSHELL_UPDATECHECK: '' + PROCESSOR_ARCHITECTURE: '' + PROCESSOR_IDENTIFIER: '' + PROCESSOR_LEVEL: '' + PROCESSOR_REVISION: '' + PSModuleAnalysisCachePath: '' + PSModulePath: '' + DOTNET_MULTILEVEL_LOOKUP: '' + DOTNET_NOLOGO: '' + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: '' + DriverData: '' + EdgeWebDriver: '' + GCM_INTERACTIVE: '' + GeckoWebDriver: '' + GHCUP_INSTALL_BASE_PREFIX: '' + GHCUP_MSYS2: '' + RUNNER_ARCH: '' + RUNNER_ENVIRONMENT: '' + RUNNER_NAME: '' + RUNNER_OS: '' + RUNNER_PERFLOG: '' + RUNNER_TEMP: '' + RUNNER_TOOL_CACHE: '' + RUNNER_TRACKING_ID: '' + RUNNER_WORKSPACE: '' + # PROGRAMFILES: '' + # ProgramW6432: '' + # ALLUSERSPROFILE: '' + # APPDATA: '' + # COMMONPROGRAMFILES: '' diff --git a/.github/workflows/desktop.apps.yml b/.github/workflows/desktop.apps.yml index ddc3a8946..47f63b1b8 100644 --- a/.github/workflows/desktop.apps.yml +++ b/.github/workflows/desktop.apps.yml @@ -54,7 +54,7 @@ jobs: run: 'yarn bootstrap' - name: Bump version desktop timer app - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const script = require('./.scripts/bump-version-electron.js') @@ -111,7 +111,7 @@ jobs: ref: master - name: Install Node.js, NPM and Yarn - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.11.1' cache: 'yarn' @@ -132,7 +132,7 @@ jobs: run: 'yarn bootstrap' - name: Bump version desktop timer app - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const script = require('./.scripts/bump-version-electron.js') @@ -189,7 +189,7 @@ jobs: ref: master - name: Install Node.js, NPM and Yarn - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.11.1' cache: 'yarn' @@ -210,7 +210,7 @@ jobs: run: 'yarn bootstrap' - name: Bump version desktop timer app - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const script = require('./.scripts/bump-version-electron.js') diff --git a/.github/workflows/extensions.dev.yml b/.github/workflows/extensions.dev.yml index 8a4813c82..1a12ab8f7 100644 --- a/.github/workflows/extensions.dev.yml +++ b/.github/workflows/extensions.dev.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/checkout@v4 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.11.1' cache: 'yarn' diff --git a/.github/workflows/extensions.prod.yml b/.github/workflows/extensions.prod.yml index 8f92f89f9..3b3136284 100644 --- a/.github/workflows/extensions.prod.yml +++ b/.github/workflows/extensions.prod.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/checkout@v4 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.11.1' cache: 'yarn' diff --git a/.github/workflows/mobile.apps.android.yml b/.github/workflows/mobile.apps.android.yml index f6db41dee..fd1d9ece6 100644 --- a/.github/workflows/mobile.apps.android.yml +++ b/.github/workflows/mobile.apps.android.yml @@ -33,7 +33,7 @@ jobs: - uses: actions/checkout@v4 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.11.1' cache: 'yarn' diff --git a/.github/workflows/mobile.apps.ios.yml b/.github/workflows/mobile.apps.ios.yml index c3fe6530b..6c6b30bdc 100644 --- a/.github/workflows/mobile.apps.ios.yml +++ b/.github/workflows/mobile.apps.ios.yml @@ -33,7 +33,7 @@ jobs: - uses: actions/checkout@v4 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.11.1' cache: 'yarn' diff --git a/.github/workflows/mobile.before-merge.yml b/.github/workflows/mobile.before-merge.yml index 5a0a60256..4b300e135 100644 --- a/.github/workflows/mobile.before-merge.yml +++ b/.github/workflows/mobile.before-merge.yml @@ -35,7 +35,7 @@ jobs: run: echo "Workflow was skipped" && exit 0 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.11.1' cache: 'yarn' diff --git a/.github/workflows/mobile.dev.yml b/.github/workflows/mobile.dev.yml index 550a05759..59911d7b9 100644 --- a/.github/workflows/mobile.dev.yml +++ b/.github/workflows/mobile.dev.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v4 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.11.1' cache: 'yarn' diff --git a/.github/workflows/mobile.prod.yml b/.github/workflows/mobile.prod.yml index e184b85a3..e471a8318 100644 --- a/.github/workflows/mobile.prod.yml +++ b/.github/workflows/mobile.prod.yml @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@v4 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.11.1' cache: 'yarn' diff --git a/.github/workflows/web.before-merge.yml b/.github/workflows/web.before-merge.yml index 2d07b1419..39ecbbbe6 100644 --- a/.github/workflows/web.before-merge.yml +++ b/.github/workflows/web.before-merge.yml @@ -33,7 +33,7 @@ jobs: run: echo "Workflow was skipped" && exit 0 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20.11.1' cache: 'yarn' diff --git a/.gitignore b/.gitignore index 376fff7cd..73bd06141 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,24 @@ Thumbs.db .netlify /.nx + +# No need to duplicate translations +/apps/server-web/src/assets/i18n + +# No need to duplicate desktop icons +/apps/server-web/src/icons + +# Generated platform logo +apps/server-web/src/assets/images/logos/platform_logo.* + +# Generated No internet logo +apps/server-web/src/assets/images/logos/no_internet_logo.* + +# Generated desktop icon tray +apps/server-web/src/assets/icons/tray + +# Generated desktop 512x512 icon +apps/server-web/src/assets/icons/desktop_logo_512x512.png + +# Generated desktop icon menu +apps/server-web/src/assets/icons/menu diff --git a/.prettierrc b/.prettierrc index 035f8a26d..1a058e296 100644 --- a/.prettierrc +++ b/.prettierrc @@ -7,6 +7,7 @@ "arrowParens": "always", "trailingComma": "none", "quoteProps": "as-needed", + "trimTrailingWhitespace": true, "overrides": [ { "files": "*.scss", diff --git a/.scripts/bump-version-electron.js b/.scripts/bump-version-electron.js new file mode 100644 index 000000000..a81d7500a --- /dev/null +++ b/.scripts/bump-version-electron.js @@ -0,0 +1,113 @@ +const fs = require('fs'); +const simpleGit = require('simple-git'); +const git = simpleGit(); + +async function getLatestTag(repoURL) { + try { + // Fetch remote tags + const tags = await git.listRemote(['--tags', repoURL]); + + // Parse and filter tags + const tagPattern = /^v?[0-9]+\.[0-9]+\.[0-9]+$/; + const tagList = tags + .split('\n') + .map((tagLine) => tagLine.split(/\s+/)[1]) // Extract the tag reference + .filter((ref) => ref && ref.includes('refs/tags/')) // Filter out non-tags + .map((ref) => ref.replace('refs/tags/', '')) // Extract the tag name + .filter((tag) => tagPattern.test(tag)); // Filter valid version tags + + // Sort and get the latest tag + const latestTag = tagList + .sort((a, b) => { + // Using localeCompare with 'numeric' option for version comparison + return a.localeCompare(b, undefined, { numeric: true }); + }) + .pop(); + + return latestTag; + } catch (error) { + console.error(`Error fetching tags: ${error.message}`); + } +} + +module.exports.serverweb = async (isProd) => { + if (fs.existsSync('./apps/server-web/src/package.json')) { + let package = require('../apps/server-web/src/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; + + const appRepoName = process.env.DESKTOP_WEB_SERVER_REPO_NAME || appName; + const appRepoOwner = process.env.DESKTOP_WEB_SERVER_REPO_OWNER || 'ever-co'; + + // For GitHub options see https://www.electron.build/configuration/publish.html + + if (!isProd) { + package.build.publish = [ + { + provider: 'github', + repo: appRepoName, + owner: appRepoOwner, + releaseType: 'prerelease' + }, + { + provider: 'spaces', + name: 'ever', + region: 'sfo3', + path: `/${appName}-pre`, + acl: 'public-read' + } + ]; + } else { + package.build.publish = [ + { + provider: 'github', + repo: appRepoName, + owner: appRepoOwner, + releaseType: 'release' + }, + { + provider: 'spaces', + name: 'ever', + region: 'sfo3', + path: `/${appName}`, + acl: 'public-read' + } + ]; + } + + fs.writeFileSync('./apps/server-web/src/package.json', JSON.stringify(package, null, 2)); + + let updated = require('../apps/server-web/src/package.json'); + + console.log('Version releasing', updated.version); + } +}; diff --git a/.snyk b/.snyk new file mode 100644 index 000000000..7c14a7b91 --- /dev/null +++ b/.snyk @@ -0,0 +1,5 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.13.5 +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: {} +patch: {} diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..44926d31e --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,11 @@ +# Security Policy + +Contact: + +Ever Teams™ follows good security practices, but 100% security cannot be guaranteed in any software! +Ever Teams™ is provided AS IS without any warranty. Use at your own risk! +See more details in the [LICENSE](LICENSE.md). + +In a production setup, all client-side to server-side (backend, APIs) communications should be encrypted using HTTPS/WSS/SSL (REST APIs, GraphQL endpoint, Socket.io WebSockets, etc.). + +If you discover any issue regarding security, please disclose the information responsibly by sending an email to and not by creating a GitHub issue. diff --git a/apps/extensions/package.json b/apps/extensions/package.json index 2d42b6ecf..36d9badc6 100644 --- a/apps/extensions/package.json +++ b/apps/extensions/package.json @@ -3,7 +3,7 @@ "displayName": "Ever Teams", "version": "0.1.0", "description": "Ever Teams Browser Extensions", - "license": "UNLICENSED", + "license": "GPL-3.0", "author": "Ever Co. LTD", "scripts": { "dev": "yarn plasmo dev", diff --git a/apps/mobile/package.json b/apps/mobile/package.json index e69b39acd..0ec2dea5f 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -1,7 +1,7 @@ { "name": "@ever-teams/mobile", "version": "0.1.0", - "license": "UNLICENSED", + "license": "GPL-3.0", "author": { "name": "Ever Co. LTD", "email": "ever@ever.co", diff --git a/apps/server-web/package.json b/apps/server-web/package.json index 816362152..0bd279f15 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-server-web", "version": "0.1.0", "description": "Ever Teams Web Server", "license": "AGPL-3.0", diff --git a/apps/web/package.json b/apps/web/package.json index 26e7904d1..b8d842d3c 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,13 +1,22 @@ { "name": "@ever-teams/web", "version": "0.1.0", - "license": "UNLICENSED", + "description": "Ever Teams - Open Work and Project Management Platform", + "license": "AGPL-3.0", + "homepage": "https://ever.team", + "repository": { + "type": "git", + "url": "https://github.com/ever-co/ever-teams.git" + }, + "bugs": { + "url": "https://github.com/ever-co/ever-teams/issues" + }, + "private": true, "author": { "name": "Ever Co. LTD", "email": "ever@ever.co", "url": "https://ever.co" }, - "private": true, "scripts": { "dev": "yarn node env.js && yarn next dev -p 3030", "build": "yarn next build", diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 000000000..e69de29bb diff --git a/dc.cmd b/dc.cmd new file mode 100644 index 000000000..2c7bc37ba --- /dev/null +++ b/dc.cmd @@ -0,0 +1,3 @@ +docker-compose down -v +docker-compose build +docker-compose up diff --git a/nx.json b/nx.json index 80e246c88..2701417e4 100644 --- a/nx.json +++ b/nx.json @@ -44,11 +44,7 @@ "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"] }, "lint": { - "inputs": [ - "default", - "{workspaceRoot}/.eslintrc.json", - "{workspaceRoot}/.eslintignore" - ] + "inputs": ["default", "{workspaceRoot}/.eslintrc.json", "{workspaceRoot}/.eslintignore"] } }, "namedInputs": { diff --git a/package.json b/package.json index 94e3351f9..a0aac5b3e 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,9 @@ { "name": "ever-teams", - "homepage": "https://ever.team", - "license": "AGPL-3.0", - "author": { - "name": "Ever Co. LTD", - "email": "ever@ever.co", - "url": "https://ever.co" - }, "version": "0.1.0", + "description": "Ever Teams - Open Work and Project Management Platform", + "license": "AGPL-3.0", + "homepage": "https://ever.team", "repository": { "type": "git", "url": "https://github.com/ever-co/ever-teams.git" @@ -16,6 +12,11 @@ "url": "https://github.com/ever-co/ever-teams/issues" }, "private": true, + "author": { + "name": "Ever Co. LTD", + "email": "ever@ever.co", + "url": "https://ever.co" + }, "scripts": { "prepare:husky": "npx husky install .husky", "ng": "cross-env NODE_ENV=development NODE_OPTIONS=--max_old_space_size=8192 yarn nx", @@ -90,7 +91,10 @@ "build:server-web": "yarn build:web:desktop && yarn prepare:server-web && yarn run copy:build:web", "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" + "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": "" }, "config": { "commitizen": { @@ -209,12 +213,51 @@ "pretty-quick": "^4.0.0", "rimraf": "^5.0.5", "semantic-release": "^22.0.12", + "simple-git": "^3.20.0", "ts-node": "^10.9.2", "@types/electron": "^1.6.10" }, + "overrides": { + "prebuild": { + "node-gyp": "$node-gyp" + }, + "iconv": { + "node-gyp": "9.3.1" + }, + "twing": { + "locutus": "2.0.30" + } + }, "engines": { "node": ">=20.11.0", "yarn": ">=1.13.0" }, + "prettier": { + "printWidth": 120, + "singleQuote": true, + "semi": true, + "useTabs": true, + "tabWidth": 4, + "arrowParens": "always", + "trailingComma": "none", + "quoteProps": "as-needed", + "trimTrailingWhitespace": true, + "overrides": [ + { + "files": "*.scss", + "options": { + "useTabs": false, + "tabWidth": 2 + } + }, + { + "files": "*.yml", + "options": { + "useTabs": false, + "tabWidth": 2 + } + } + ] + }, "snyk": true } diff --git a/yarn.lock b/yarn.lock index d6a9c2c9d..6b4736944 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3179,6 +3179,18 @@ resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60" integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA== +"@kwsites/file-exists@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@kwsites/file-exists/-/file-exists-1.1.1.tgz#ad1efcac13e1987d8dbaf235ef3be5b0d96faa99" + integrity sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw== + dependencies: + debug "^4.1.1" + +"@kwsites/promise-deferred@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz#8ace5259254426ccef57f3175bc64ed7095ed919" + integrity sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw== + "@leichtgewicht/ip-codec@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" @@ -11649,7 +11661,7 @@ debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.0.1, debug@^4.3.1: +debug@^4.0.1, debug@^4.3.1, debug@^4.3.5: version "4.3.5" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== @@ -22904,6 +22916,15 @@ simple-get@^4.0.0, simple-get@^4.0.1: once "^1.3.1" simple-concat "^1.0.0" +simple-git@^3.20.0: + version "3.25.0" + resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-3.25.0.tgz#3666e76d6831f0583dc380645945b97e0ac4aab6" + integrity sha512-KIY5sBnzc4yEcJXW7Tdv4viEz8KyG+nU0hay+DWZasvdFOYKeUZ6Xc25LUHHjw0tinPT7O1eY6pzX7pRT1K8rw== + dependencies: + "@kwsites/file-exists" "^1.1.1" + "@kwsites/promise-deferred" "^1.1.1" + debug "^4.3.5" + simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"