From 3e87aaa17af0315be2a66fd636a49c12ca48a887 Mon Sep 17 00:00:00 2001 From: Piyush Kumar Date: Wed, 13 Mar 2024 11:28:54 +0530 Subject: [PATCH 1/8] fix(user-management): update role to displayName --- .../user-management/user-access-resource.tsx | 15 ++++++++++++++- .../console/server/gql/queries/router-queries.ts | 10 +++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/apps/console/routes/_main+/$account+/settings+/user-management/user-access-resource.tsx b/src/apps/console/routes/_main+/$account+/settings+/user-management/user-access-resource.tsx index 22a64d7af..653030b45 100644 --- a/src/apps/console/routes/_main+/$account+/settings+/user-management/user-access-resource.tsx +++ b/src/apps/console/routes/_main+/$account+/settings+/user-management/user-access-resource.tsx @@ -32,6 +32,19 @@ interface IResource { onDelete: (item: BaseType) => void; } +const mapRoleToDisplayName = (role: string): string => { + switch (role) { + case 'account_owner': + return 'owner'; + case 'account_member': + return 'member'; + case 'account_admin': + return 'admin'; + default: + return role; + } +}; + const ExtraButton = ({ onDelete }: { onDelete: () => void }) => { return ( { }, { key: 2, - render: () => , + render: () => , }, { key: 3, diff --git a/src/apps/console/server/gql/queries/router-queries.ts b/src/apps/console/server/gql/queries/router-queries.ts index 3edccef7a..45fac68a3 100644 --- a/src/apps/console/server/gql/queries/router-queries.ts +++ b/src/apps/console/server/gql/queries/router-queries.ts @@ -37,7 +37,7 @@ export const routerQueries = (executor: IExecutor) => ({ { transformer: (data: ConsoleCreateRouterMutation) => data.core_createRouter, - vars(_: ConsoleCreateRouterMutationVariables) { }, + vars(_: ConsoleCreateRouterMutationVariables) {}, } ), updateRouter: executor( @@ -59,7 +59,7 @@ export const routerQueries = (executor: IExecutor) => ({ { transformer: (data: ConsoleUpdateRouterMutation) => data.core_updateRouter, - vars(_: ConsoleUpdateRouterMutationVariables) { }, + vars(_: ConsoleUpdateRouterMutationVariables) {}, } ), deleteRouter: executor( @@ -79,7 +79,7 @@ export const routerQueries = (executor: IExecutor) => ({ { transformer: (data: ConsoleDeleteRouterMutation) => data.core_deleteRouter, - vars(_: ConsoleDeleteRouterMutationVariables) { }, + vars(_: ConsoleDeleteRouterMutationVariables) {}, } ), listRouters: executor( @@ -193,7 +193,7 @@ export const routerQueries = (executor: IExecutor) => ({ `, { transformer: (data: ConsoleListRoutersQuery) => data.core_listRouters, - vars(_: ConsoleListRoutersQueryVariables) { }, + vars(_: ConsoleListRoutersQueryVariables) {}, } ), getRouter: executor( @@ -283,7 +283,7 @@ export const routerQueries = (executor: IExecutor) => ({ `, { transformer: (data: ConsoleGetRouterQuery) => data.core_getRouter, - vars(_: ConsoleGetRouterQueryVariables) { }, + vars(_: ConsoleGetRouterQueryVariables) {}, } ), }); From 88fff190b3f74e97a584a218675fdc3fe991f845 Mon Sep 17 00:00:00 2001 From: Piyush Kumar Date: Wed, 13 Mar 2024 17:32:26 +0530 Subject: [PATCH 2/8] fix(user-management): - user can edit role for members - only remove button will require for pending invitations --- .../infra+/$cluster+/helm-charts/_index.tsx | 11 +- .../settings+/user-management/handle-user.tsx | 166 ++++++++++-------- .../settings+/user-management/route.tsx | 26 ++- .../user-management/user-access-resource.tsx | 138 +++++++++++---- 4 files changed, 215 insertions(+), 126 deletions(-) diff --git a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/_index.tsx b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/_index.tsx index 5de217bb9..ec6e73341 100644 --- a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/_index.tsx +++ b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/_index.tsx @@ -8,10 +8,10 @@ import Wrapper from '~/console/components/wrapper'; import { GQLServerHandler } from '~/console/server/gql/saved-queries'; import { parseNodes } from '~/console/server/r-utils/common'; import { IRemixCtx } from '~/root/lib/types/common'; +import fake from '~/root/fake-data-generator/fake'; import Tools from './tools'; import HelmChartResources from './helm-chart-resources'; import HandleHelmChart from './handle-helm-chart'; -import fake from "~/root/fake-data-generator/fake"; export const loader = (ctx: IRemixCtx) => { const { cluster } = ctx.params; @@ -38,10 +38,11 @@ const HelmCharts = () => { return ( <> {({ helmChartData }) => { const helmCharts = parseNodes(helmChartData); diff --git a/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx b/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx index f38daa433..689c03856 100644 --- a/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx +++ b/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx @@ -3,42 +3,55 @@ import { TextInput } from '~/components/atoms/input'; import SelectPrimitive from '~/components/atoms/select-primitive'; import Popup from '~/components/molecule/popup'; import { toast } from '~/components/molecule/toast'; -import { IDialog } from '~/console/components/types.d'; +import { IDialogBase } from '~/console/components/types.d'; import { useConsoleApi } from '~/console/server/gql/api-provider'; import useForm from '~/root/lib/client/hooks/use-form'; import Yup from '~/root/lib/server/helpers/yup'; import { handleError } from '~/root/lib/utils/common'; import { Github__Com___Kloudlite___Api___Apps___Iam___Types__Role as Role } from '~/root/src/generated/gql/server'; import { parseName } from '~/console/server/r-utils/common'; +import CommonPopupHandle from '~/console/components/common-popup-handle'; +import { + IMemberType, + mapRoleToDisplayName, +} from '~/console/routes/_main+/$account+/settings+/user-management/user-access-resource'; import { IAccountContext } from '../../_layout'; +type IDialog = IDialogBase; + const validRoles = (role: string): Role => { - switch (role as Role) { - case 'account_owner': - case 'account_admin': - case 'account_member': - return role as Role; + switch (role) { + case 'owner': + return 'account_owner' as Role; + case 'member': + return 'account_member' as Role; default: throw new Error(`invalid role ${role}`); } }; -const HandleUser = ({ show, setShow }: IDialog) => { +const Root = (props: IDialog) => { + const { setVisible, isUpdate } = props; const api = useConsoleApi(); const { account } = useOutletContext(); - const { values, handleChange, handleSubmit, resetValues, isLoading } = - useForm({ - initialValues: { - email: '', - role: 'account_member', - }, - validationSchema: Yup.object({ - email: Yup.string().required().email(), - }), - onSubmit: async (val) => { - try { + const { values, handleChange, handleSubmit, isLoading } = useForm({ + initialValues: isUpdate + ? { + email: props?.data.email || '', + role: mapRoleToDisplayName(props?.data.role) || 'member', + } + : { + email: '', + role: 'member', + }, + validationSchema: Yup.object({ + email: Yup.string().required().email(), + }), + onSubmit: async (val) => { + try { + if (!isUpdate) { const { errors: e } = await api.inviteMembersForAccount({ accountName: parseName(account), invitations: { @@ -49,68 +62,81 @@ const HandleUser = ({ show, setShow }: IDialog) => { if (e) { throw e[0]; } - toast.success('user invited'); - setShow(null); - } catch (err) { - handleError(err); + } else if (isUpdate) { + const { errors: e } = await api.updateAccountMembership({ + accountName: parseName(account), + memberId: props?.data.id, + role: validRoles(val.role), + }); + if (e) { + throw e[0]; + } } - }, - }); + toast.success(`user ${isUpdate ? 'role updated' : 'invited'}`); + setVisible(false); + } catch (err) { + handleError(err); + } + }, + }); - const roles: Role[] = ['account_owner', 'account_admin', 'account_member']; + const roles: string[] = ['owner', 'member']; return ( - { - if (!e) { - resetValues(); - } - - setShow(e); - }} - > - Invite user -
- -
-
+ + +
+
+ {!isUpdate ? ( -
- - - - -- not-selected -- - - {roles.map((role) => { - return ( - - {role} - - ); - })} - + ) : ( + + )}
-
- - - - - - + + + + -- not-selected -- + + {roles.map((role) => { + return ( + + {role} + + ); + })} + +
+ + + + + + + ); +}; + +const HandleUser = (props: IDialog) => { + return ( + ); }; diff --git a/src/apps/console/routes/_main+/$account+/settings+/user-management/route.tsx b/src/apps/console/routes/_main+/$account+/settings+/user-management/route.tsx index 55c77fd00..c46fc7e8c 100644 --- a/src/apps/console/routes/_main+/$account+/settings+/user-management/route.tsx +++ b/src/apps/console/routes/_main+/$account+/settings+/user-management/route.tsx @@ -5,10 +5,8 @@ import { Button } from '~/components/atoms/button'; import Profile from '~/components/molecule/profile'; import ExtendedFilledTab from '~/console/components/extended-filled-tab'; import SecondarySubHeader from '~/console/components/secondary-sub-header'; -import { IShowDialog } from '~/console/components/types.d'; import Wrapper from '~/console/components/wrapper'; import { useConsoleApi } from '~/console/server/gql/api-provider'; -import { DIALOG_DATA_NONE } from '~/console/utils/commons'; import { useSearch } from '~/root/lib/client/helpers/search-filter'; import { NonNullableString } from '~/root/lib/types/common'; import Pulsable from '~/console/components/pulsable'; @@ -22,7 +20,7 @@ import Tools from './tools'; import UserAccessResources from './user-access-resource'; interface ITeams { - setShowUserInvite: React.Dispatch>; + setShowUserInvite: React.Dispatch>; searchText: string; } @@ -78,7 +76,7 @@ const Teams = ({ setShowUserInvite, searchText }: ITeams) => { content: 'Invite users', prefix: , onClick: () => { - setShowUserInvite(DIALOG_DATA_NONE); + setShowUserInvite(true); }, }, }} @@ -105,6 +103,7 @@ const Teams = ({ setShowUserInvite, searchText }: ITeams) => { email: i.user.email, })) } + isPendingInvitation={false} /> )} @@ -159,7 +158,7 @@ const Invitations = ({ setShowUserInvite, searchText }: ITeams) => { content: 'Invite users', prefix: , onClick: () => { - setShowUserInvite(DIALOG_DATA_NONE); + setShowUserInvite(true); }, }, }} @@ -178,6 +177,7 @@ const Invitations = ({ setShowUserInvite, searchText }: ITeams) => { id: i.id || i.userEmail || '', })) } + isPendingInvitation /> @@ -189,7 +189,7 @@ const SettingUserManagement = () => { const [active, setActive] = useState< 'team' | 'invitations' | NonNullableString >('team'); - const [showUserInvite, setShowUserInvite] = useState(null); + const [visible, setVisible] = useState(false); const { account } = useOutletContext(); const [searchText, setSearchText] = useState(''); @@ -219,7 +219,7 @@ const SettingUserManagement = () => {
{active === 'team' ? ( - + ) : ( - + )} - + ); }; diff --git a/src/apps/console/routes/_main+/$account+/settings+/user-management/user-access-resource.tsx b/src/apps/console/routes/_main+/$account+/settings+/user-management/user-access-resource.tsx index 653030b45..dda01e1a7 100644 --- a/src/apps/console/routes/_main+/$account+/settings+/user-management/user-access-resource.tsx +++ b/src/apps/console/routes/_main+/$account+/settings+/user-management/user-access-resource.tsx @@ -1,4 +1,4 @@ -import { Trash } from '@jengaicons/react'; +import { PencilSimple, Trash } from '@jengaicons/react'; import { useOutletContext } from '@remix-run/react'; import { useState } from 'react'; import { Avatar } from '~/components/atoms/avatar'; @@ -11,11 +11,14 @@ import { import DeleteDialog from '~/console/components/delete-dialog'; import List from '~/console/components/list'; import ListGridView from '~/console/components/list-grid-view'; -import ResourceExtraAction from '~/console/components/resource-extra-action'; +import ResourceExtraAction, { + IResourceExtraItem, +} from '~/console/components/resource-extra-action'; import { useConsoleApi } from '~/console/server/gql/api-provider'; import { useReload } from '~/root/lib/client/helpers/reloader'; import { handleError } from '~/root/lib/utils/common'; import { parseName } from '~/console/server/r-utils/common'; +import HandleUser from '~/console/routes/_main+/$account+/settings+/user-management/handle-user'; import { IAccountContext } from '../../_layout'; const RESOURCE_NAME = 'user'; @@ -26,43 +29,66 @@ type BaseType = { role: string; email: string; }; +export type IMemberType = BaseType; -interface IResource { - items: BaseType[]; - onDelete: (item: BaseType) => void; -} +type OnAction = ({ + action, + item, +}: { + action: 'delete' | 'edit'; + item: BaseType; +}) => void; + +type IExtraButton = { + onAction: OnAction; + item: BaseType; + isInvite: boolean; +}; -const mapRoleToDisplayName = (role: string): string => { +export const mapRoleToDisplayName = (role: string): string => { switch (role) { case 'account_owner': return 'owner'; case 'account_member': return 'member'; - case 'account_admin': - return 'admin'; default: return role; } }; -const ExtraButton = ({ onDelete }: { onDelete: () => void }) => { - return ( - , - type: 'item', - onClick: onDelete, - key: 'remove', - className: '!text-text-critical', - }, - ]} - /> - ); +const ExtraButton = ({ onAction, item, isInvite }: IExtraButton) => { + let items: IResourceExtraItem[] = [ + { + label: 'Remove', + icon: , + type: 'item', + onClick: () => onAction({ action: 'delete', item }), + key: 'remove', + className: '!text-text-critical', + }, + ]; + if (!isInvite) { + items = [ + { + label: 'Edit', + icon: , + type: 'item', + onClick: () => onAction({ action: 'edit', item }), + key: 'edit', + }, + ...items, + ]; + } + return ; }; -const ListView = ({ items = [], onDelete }: IResource) => { +interface IResource { + items: BaseType[]; + onAction: OnAction; + isInvite: boolean; +} + +const ListView = ({ items = [], onAction, isInvite }: IResource) => { return ( {items.map((item) => ( @@ -87,7 +113,13 @@ const ListView = ({ items = [], onDelete }: IResource) => { }, { key: 3, - render: () => onDelete(item)} />, + render: () => ( + + ), }, ]} /> @@ -96,10 +128,17 @@ const ListView = ({ items = [], onDelete }: IResource) => { ); }; -const UserAccessResources = ({ items = [] }: { items: BaseType[] }) => { +const UserAccessResources = ({ + items = [], + isPendingInvitation = false, +}: { + items: BaseType[]; + isPendingInvitation: boolean; +}) => { const [showDeleteDialog, setShowDeleteDialog] = useState( null ); + const [showUserInvite, setShowUserInvite] = useState(null); const { account } = useOutletContext(); @@ -108,16 +147,35 @@ const UserAccessResources = ({ items = [] }: { items: BaseType[] }) => { const props: IResource = { items, - onDelete: (item) => { - setShowDeleteDialog(item); + isInvite: isPendingInvitation, + onAction: ({ action, item }) => { + switch (action) { + case 'edit': + setShowUserInvite(item); + break; + case 'delete': + setShowDeleteDialog(item); + break; + default: + break; + } }, }; + return ( <> } gridView={} /> + setShowUserInvite(null), + visible: !!showUserInvite, + }} + /> { setShow={setShowDeleteDialog} onSubmit={async () => { try { - const { errors } = await api.deleteAccountMembership({ - accountName: parseName(account), - memberId: showDeleteDialog!.id, - }); - if (errors) { - throw errors[0]; + if (!isPendingInvitation) { + const { errors } = await api.deleteAccountMembership({ + accountName: parseName(account), + memberId: showDeleteDialog!.id, + }); + if (errors) { + throw errors[0]; + } + } else if (isPendingInvitation) { + const { errors } = await api.deleteAccountInvitation({ + accountName: parseName(account), + invitationId: showDeleteDialog!.id, + }); + if (errors) { + throw errors[0]; + } } reloadPage(); toast.success(`${titleCase(RESOURCE_NAME)} deleted successfully`); From 820495686a40e4b5c3df4d6007afb274452ff65d Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Wed, 13 Mar 2024 15:31:44 +0530 Subject: [PATCH 3/8] :art: Improved error handling for serverside --- Taskfile.yaml | 2 +- lib/app-setup/root.tsx | 13 ++++-- lib/client/helpers/log.ts | 4 +- lib/client/hooks/use-custom-loader-data.tsx | 12 ++++++ .../helpers/execute-query-with-context.ts | 4 +- lib/utils/common.tsx | 24 +++++++++-- src/apps/auth/routes/_main+/logout.tsx | 41 +++++++++---------- src/apps/auth/routes/_main+/verify-email.tsx | 6 ++- 8 files changed, 71 insertions(+), 35 deletions(-) create mode 100644 lib/client/hooks/use-custom-loader-data.tsx diff --git a/Taskfile.yaml b/Taskfile.yaml index cc9bb95da..41360ec3b 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -8,7 +8,7 @@ tasks: interactive: true cmds: - | - BASE_URL=gcp-production.kloudlite.io + BASE_URL=dev.kloudlite.io COOKIE_DOMAIN=".kloudlite.io" GATEWAY_URL="http://gateway.kloudlite.svc.cluster.local" case {{.app}} in diff --git a/lib/app-setup/root.tsx b/lib/app-setup/root.tsx index 696bf1f5f..7c22f3087 100644 --- a/lib/app-setup/root.tsx +++ b/lib/app-setup/root.tsx @@ -32,6 +32,7 @@ import tailwindBase from '~/design-system/tailwind-base.js'; import { ReloadIndicator } from '~/lib/client/components/reload-indicator'; import { isDev } from '~/lib/client/helpers/log'; import { getClientEnv, getServerEnv } from '../configs/base-url.cjs'; +import { useDataFromMatches } from '../client/hooks/use-custom-matches'; export const links: LinksFunction = () => [ { rel: 'stylesheet', href: stylesUrl }, @@ -167,6 +168,8 @@ const Root = ({ }) => { const env = useLoaderData(); + const error = useDataFromMatches('error', ''); + return ( @@ -235,9 +238,13 @@ const Root = ({ - - - + {error ? ( +
{JSON.stringify(error)}
+ ) : ( + + + + )} diff --git a/lib/client/helpers/log.ts b/lib/client/helpers/log.ts index 0e18c6651..63ef9b663 100644 --- a/lib/client/helpers/log.ts +++ b/lib/client/helpers/log.ts @@ -90,12 +90,12 @@ const logger = { } if (err) { - console.log(err); + console.trace(`\n\n${err}\n\n`); if (!isDev) { PostToHook(`\`\`\`${err}\`\`\``); } } else { - console.trace(args); + console.trace(`\n\n${args}\n\n`); } if (isDev && typeof window === 'undefined') { diff --git a/lib/client/hooks/use-custom-loader-data.tsx b/lib/client/hooks/use-custom-loader-data.tsx new file mode 100644 index 000000000..75b2088be --- /dev/null +++ b/lib/client/hooks/use-custom-loader-data.tsx @@ -0,0 +1,12 @@ +import { useLoaderData } from '@remix-run/react'; + +type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( + k: infer I +) => void + ? I + : never; + +const useExtLoaderData = Promise>() => + useLoaderData() as UnionToIntersection>>; + +export default useExtLoaderData; diff --git a/lib/server/helpers/execute-query-with-context.ts b/lib/server/helpers/execute-query-with-context.ts index 91f24e7da..43eea48a6 100644 --- a/lib/server/helpers/execute-query-with-context.ts +++ b/lib/server/helpers/execute-query-with-context.ts @@ -112,12 +112,12 @@ export const ExecuteQueryWithContext = ( return { ...resp.data, data }; } catch (err) { if ((err as AxiosError).response) { - console.trace('ErrorIn:', apiName, (err as Error).name); + console.log('\nErrorIn:', apiName, (err as Error).name, '\n'); return (err as AxiosError).response?.data; } - console.trace('ErrorIn:', apiName, (err as Error).message); + console.log('\nErrorIn:', apiName, (err as Error).message, '\n'); return { data: null, diff --git a/lib/utils/common.tsx b/lib/utils/common.tsx index fb71ad287..d731753fa 100644 --- a/lib/utils/common.tsx +++ b/lib/utils/common.tsx @@ -1,10 +1,27 @@ import { toast } from '~/components/molecule/toast'; import logger from '../client/helpers/log'; -export const handleError = (e: unknown): void => { +export const handleError = ( + e: unknown +): { + error?: { + message: string; + }; +} => { + if (typeof window === 'undefined') { + const a = e as Error; + return { + error: { + message: a.message, + }, + }; + } + const err = e as Error; toast.error(err.message); logger.error(e); + + return {}; }; export const parseError = (e: unknown): Error => { @@ -27,8 +44,9 @@ export const Truncate = ({ }; export function sleep(time: number) { - // eslint-disable-next-line no-promise-executor-return - return new Promise((resolve) => setTimeout(resolve, time)); + return new Promise((resolve) => { + setTimeout(resolve, time); + }); } export const anyUndefined: any = undefined; diff --git a/src/apps/auth/routes/_main+/logout.tsx b/src/apps/auth/routes/_main+/logout.tsx index 6d06933d7..9a34770b9 100644 --- a/src/apps/auth/routes/_main+/logout.tsx +++ b/src/apps/auth/routes/_main+/logout.tsx @@ -1,13 +1,27 @@ -import { getCookie } from '~/root/lib/app-setup/cookies'; -import withContext from '~/root/lib/app-setup/with-contxt'; -import { useNavigate, useLoaderData } from '@remix-run/react'; -import { useEffect } from 'react'; +import { useNavigate } from '@remix-run/react'; import { BrandLogo } from '~/components/branding/brand-logo'; import { IExtRemixCtx } from '~/root/lib/types/common'; +import { GQLServerHandler } from '~/auth/server/gql/saved-queries'; +import { handleError } from '~/root/lib/utils/common'; +import useExtLoaderData from '~/root/lib/client/hooks/use-custom-loader-data'; +import { useEffect } from 'react'; + +export const loader = async (ctx: IExtRemixCtx) => { + const { errors } = await GQLServerHandler(ctx.request).logout(); + + if (errors) { + return handleError(errors[0]); + } + + return { + done: true, + }; +}; const LogoutPage = () => { const navigate = useNavigate(); - const { done } = useLoaderData(); + + const { done } = useExtLoaderData(); useEffect(() => { if (done) { @@ -22,21 +36,4 @@ const LogoutPage = () => { ); }; -export const loader = async (ctx: IExtRemixCtx) => { - const cookie = getCookie(ctx); - - const keys = Object.keys(cookie.getAll()); - - for (let i = 0; i < keys.length; i += 1) { - const key = keys[i]; - if (key === 'hotspot-session') { - cookie.remove(key); - } - } - - return withContext(ctx, { - done: 'true', - }); -}; - export default LogoutPage; diff --git a/src/apps/auth/routes/_main+/verify-email.tsx b/src/apps/auth/routes/_main+/verify-email.tsx index c34b6e724..b85459f96 100644 --- a/src/apps/auth/routes/_main+/verify-email.tsx +++ b/src/apps/auth/routes/_main+/verify-email.tsx @@ -46,6 +46,7 @@ const VerifyEmail = () => { (async () => { try { if (!email) { + // TODO: handle this case, by taking email from user toast.error('Something went wrong! Please try again.'); return; } @@ -137,8 +138,9 @@ export const loader = async (ctx: IRemixCtx) => { const query = getQueries(ctx); const { data, errors } = await GQLServerHandler(ctx.request).whoAmI(); if (errors) { - console.error(errors[0].message); - return redirect('/'); + return { + query, + }; } const { email, verified } = data || {}; From efd6d3600ee811d82f1c57c7b35fe28f61c09a60 Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Wed, 13 Mar 2024 16:53:22 +0530 Subject: [PATCH 4/8] :bug: Fixed issue with logs, state [loading,subscribed] --- lib/client/components/logger/index.tsx | 4 +- lib/client/helpers/log.ts | 52 +++------------------ lib/client/helpers/socket/context.tsx | 24 ++++++---- lib/client/helpers/socket/useSockLogs.tsx | 14 +----- lib/client/hooks/use-custom-loader-data.tsx | 4 +- lib/utils/common.tsx | 6 +-- src/apps/auth/routes/_main+/logout.tsx | 47 ++++++++++--------- 7 files changed, 52 insertions(+), 99 deletions(-) diff --git a/lib/client/components/logger/index.tsx b/lib/client/components/logger/index.tsx index 85bd5c3ea..f85dd9a34 100644 --- a/lib/client/components/logger/index.tsx +++ b/lib/client/components/logger/index.tsx @@ -669,7 +669,7 @@ const LogComp = ({ } }, [fullScreen]); - const { logs, subscribed, errors } = useSocketLogs(websocket); + const { logs, subscribed, errors, isLoading } = useSocketLogs(websocket); const [isClientSide, setIsClientSide] = useState(false); @@ -744,7 +744,7 @@ const LogComp = ({ )} - {!subscribed && logs.length === 0 && } + {isLoading && } {errors.length ? (
{JSON.stringify(errors)}
diff --git a/lib/client/helpers/log.ts b/lib/client/helpers/log.ts index 63ef9b663..ed4525f45 100644 --- a/lib/client/helpers/log.ts +++ b/lib/client/helpers/log.ts @@ -1,7 +1,4 @@ -// import axios from 'axios'; -// import { consoleBaseUrl } from '../../configs/base-url.cjs'; import { serverError } from '../../server/helpers/server-error'; -// import { parseError } from '../../utils/common'; const getNodeEnv = () => { const env = (() => { @@ -19,43 +16,6 @@ const getNodeEnv = () => { return 'development'; }; -/* eslint-disable no-unused-vars */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -export const PostErr = async (message: string, source: string) => { - // try { - // await axios.post( - // 'https://hooks.slack.com/services/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', - // { - // body: { - // channel: source === 'server' ? '#bugs' : '#web-errors', - // username: - // typeof window === 'undefined' ? 'server-error' : 'web-error', - // text: message, - // icon_emoji: ':ghost:', - // }, - // } - // ); - // } catch (err) { - // console.log(parseError(err).message); - // } - return {}; -}; - -const PostToHook = (message: string) => { - // if (typeof window === 'undefined') { - // return PostErr(message, 'server'); - // } - // - // try { - // axios.post(`${consoleBaseUrl}/api/error`, { - // body: { error: message }, - // }); - // } catch (err) { - // console.log(err); - // } - return {}; -}; - export const isDev = getNodeEnv() === 'development'; const logger = { @@ -63,8 +23,6 @@ const logger = { timeEnd: isDev ? console.timeEnd : () => {}, log: isDev ? console.log : () => {}, - // log: console.log, - warn: console.warn, trace: (...args: any[]) => { let err; @@ -90,14 +48,16 @@ const logger = { } if (err) { - console.trace(`\n\n${err}\n\n`); if (!isDev) { - PostToHook(`\`\`\`${err}\`\`\``); + console.trace(`\n\n${args}\n\n`); + return; } - } else { - console.trace(`\n\n${args}\n\n`); + console.error(`\n\n${err}\n\n`); + return; } + console.trace(`\n\n${args}\n\n`); + if (isDev && typeof window === 'undefined') { serverError(args); } diff --git a/lib/client/helpers/socket/context.tsx b/lib/client/helpers/socket/context.tsx index e4d3859be..07c66fecb 100644 --- a/lib/client/helpers/socket/context.tsx +++ b/lib/client/helpers/socket/context.tsx @@ -62,8 +62,8 @@ export const useSubscribe = ( const { sendMsg, responses, - infos: i, - errors: e, + infos: mInfos, + errors: mErrors, clear, } = useContext(Context); @@ -85,9 +85,10 @@ export const useSubscribe = ( const m = msg[k]; tr.push(...(responses[m.for]?.[m.data.id || 'default'] || [])); - terr.push(...(e[m.for]?.[m.data.id || 'default'] || [])); - ti.push(...(i[m.for]?.[m.data.id || 'default'] || [])); + terr.push(...(mErrors[m.for]?.[m.data.id || 'default'] || [])); + ti.push(...(mInfos[m.for]?.[m.data.id || 'default'] || [])); } + setResp(tr); setErrors(terr); setInfos(ti); @@ -97,16 +98,19 @@ export const useSubscribe = ( } return; } + const tempResp = responses[msg.for]?.[msg.data.id || 'default'] || []; + setResp(tempResp); + + setErrors(mErrors[msg.for]?.[msg.data.id || 'default'] || []); - setResp(responses[msg.for]?.[msg.data.id || 'default'] || []); - setErrors(e[msg.for]?.[msg.data.id || 'default'] || []); - setInfos(i[msg.for]?.[msg.data.id || 'default'] || []); + const tempInfo = mInfos[msg.for]?.[msg.data.id || 'default'] || []; + setInfos(tempInfo); - if (resp.length || i[msg.for]?.[msg.data.id || 'default']?.length) { + if (tempResp.length || tempInfo.length) { setSubscribed(true); } })(); - }, [responses]); + }, [responses, mInfos, mErrors]); useDebounce( () => { @@ -236,7 +240,7 @@ export const SockProvider = ({ children }: ChildrenProps) => { }; w.onerror = (e) => { - console.error(e); + console.error('socket closed:', e); if (!rejected) { rejected = true; rej(e); diff --git a/lib/client/helpers/socket/useSockLogs.tsx b/lib/client/helpers/socket/useSockLogs.tsx index 81e28e5cc..b1831166f 100644 --- a/lib/client/helpers/socket/useSockLogs.tsx +++ b/lib/client/helpers/socket/useSockLogs.tsx @@ -11,7 +11,7 @@ interface IuseLog { export const useSocketLogs = ({ account, cluster, trackingId }: IuseLog) => { const [logs, setLogs] = useState[]>([]); - const { responses, subscribed, errors } = useSubscribe( + const { responses, infos, subscribed, errors } = useSubscribe( { for: 'logs', data: { @@ -26,16 +26,6 @@ export const useSocketLogs = ({ account, cluster, trackingId }: IuseLog) => { [] ); - const [isLoading, setIsLoading] = useState(true); - - useEffect(() => { - if (subscribed && isLoading) { - setIsLoading(false); - } else if (!subscribed && !isLoading) { - setIsLoading(true); - } - }, []); - useEffect(() => { const sorted = responses.sort((a, b) => { const resp = b.data.podName.localeCompare(a.data.podName); @@ -55,7 +45,7 @@ export const useSocketLogs = ({ account, cluster, trackingId }: IuseLog) => { return { logs, errors, - isLoading, + isLoading: !subscribed && (logs.length === 0 || infos.length === 0), subscribed, }; }; diff --git a/lib/client/hooks/use-custom-loader-data.tsx b/lib/client/hooks/use-custom-loader-data.tsx index 75b2088be..40ca49199 100644 --- a/lib/client/hooks/use-custom-loader-data.tsx +++ b/lib/client/hooks/use-custom-loader-data.tsx @@ -6,7 +6,5 @@ type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( ? I : never; -const useExtLoaderData = Promise>() => +export const useExtLoaderData = Promise>() => useLoaderData() as UnionToIntersection>>; - -export default useExtLoaderData; diff --git a/lib/utils/common.tsx b/lib/utils/common.tsx index d731753fa..38469880d 100644 --- a/lib/utils/common.tsx +++ b/lib/utils/common.tsx @@ -8,16 +8,16 @@ export const handleError = ( message: string; }; } => { + const err = e as Error; + if (typeof window === 'undefined') { - const a = e as Error; return { error: { - message: a.message, + message: err.message, }, }; } - const err = e as Error; toast.error(err.message); logger.error(e); diff --git a/src/apps/auth/routes/_main+/logout.tsx b/src/apps/auth/routes/_main+/logout.tsx index 9a34770b9..ecea7e1a8 100644 --- a/src/apps/auth/routes/_main+/logout.tsx +++ b/src/apps/auth/routes/_main+/logout.tsx @@ -1,33 +1,34 @@ import { useNavigate } from '@remix-run/react'; import { BrandLogo } from '~/components/branding/brand-logo'; -import { IExtRemixCtx } from '~/root/lib/types/common'; -import { GQLServerHandler } from '~/auth/server/gql/saved-queries'; -import { handleError } from '~/root/lib/utils/common'; -import useExtLoaderData from '~/root/lib/client/hooks/use-custom-loader-data'; -import { useEffect } from 'react'; - -export const loader = async (ctx: IExtRemixCtx) => { - const { errors } = await GQLServerHandler(ctx.request).logout(); - - if (errors) { - return handleError(errors[0]); - } - - return { - done: true, - }; -}; +import { handleError, sleep } from '~/root/lib/utils/common'; +import { useAuthApi } from '~/auth/server/gql/api-provider'; +import { toast } from 'react-toastify'; +import useDebounce from '~/root/lib/client/hooks/use-debounce'; const LogoutPage = () => { const navigate = useNavigate(); + const api = useAuthApi(); - const { done } = useExtLoaderData(); + useDebounce( + () => { + (async () => { + try { + const { errors } = await api.logout({}); + if (errors) { + throw errors[0]; + } - useEffect(() => { - if (done) { - navigate('/'); - } - }, [done]); + toast.warn('Logged out successfully'); + await sleep(1000); + navigate('/login'); + } catch (error) { + handleError(error); + } + })(); + }, + 1000, + [] + ); return (
From 2818b1c347c3d82ab93e50d1b768b89b162b0559 Mon Sep 17 00:00:00 2001 From: Piyush Kumar Date: Wed, 13 Mar 2024 19:50:56 +0530 Subject: [PATCH 5/8] minor refactoring --- .editorconfig | 0 .../$account+/infra+/$cluster+/helm-charts/_index.tsx | 11 +++++------ 2 files changed, 5 insertions(+), 6 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..e69de29bb diff --git a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/_index.tsx b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/_index.tsx index ec6e73341..5de217bb9 100644 --- a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/_index.tsx +++ b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/_index.tsx @@ -8,10 +8,10 @@ import Wrapper from '~/console/components/wrapper'; import { GQLServerHandler } from '~/console/server/gql/saved-queries'; import { parseNodes } from '~/console/server/r-utils/common'; import { IRemixCtx } from '~/root/lib/types/common'; -import fake from '~/root/fake-data-generator/fake'; import Tools from './tools'; import HelmChartResources from './helm-chart-resources'; import HandleHelmChart from './handle-helm-chart'; +import fake from "~/root/fake-data-generator/fake"; export const loader = (ctx: IRemixCtx) => { const { cluster } = ctx.params; @@ -38,11 +38,10 @@ const HelmCharts = () => { return ( <> {({ helmChartData }) => { const helmCharts = parseNodes(helmChartData); From b81429a83ab6743e794f817e295a07dc44cde056 Mon Sep 17 00:00:00 2001 From: Piyush Kumar Date: Wed, 13 Mar 2024 20:18:52 +0530 Subject: [PATCH 6/8] minor refactoring --- .../settings+/user-management/handle-user.tsx | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx b/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx index 689c03856..5c624b914 100644 --- a/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx +++ b/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx @@ -15,6 +15,7 @@ import { IMemberType, mapRoleToDisplayName, } from '~/console/routes/_main+/$account+/settings+/user-management/user-access-resource'; +import { useState } from 'react'; import { IAccountContext } from '../../_layout'; type IDialog = IDialogBase; @@ -35,6 +36,7 @@ const Root = (props: IDialog) => { const api = useConsoleApi(); const { account } = useOutletContext(); + const [inputName, setInputName] = useState(''); const { values, handleChange, handleSubmit, isLoading } = useForm({ initialValues: isUpdate @@ -115,6 +117,23 @@ const Root = (props: IDialog) => { })}
+ {isUpdate && ( +
+
+ Type in confirm to continue. +
+
+ { + setInputName(target.value); + }} + // disabled={loading} + autoComplete="off" + /> +
+
+ )}
@@ -123,6 +142,7 @@ const Root = (props: IDialog) => { type="submit" content={isUpdate ? 'update' : 'Send invite'} variant="primary" + disabled={isUpdate ? inputName !== 'confirm' : false} /> From 24c166c995f166487758587a7cb46f8576f9d9fe Mon Sep 17 00:00:00 2001 From: Piyush Kumar Date: Wed, 13 Mar 2024 20:42:57 +0530 Subject: [PATCH 7/8] remove confirm text input from update user role --- .../settings+/user-management/handle-user.tsx | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx b/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx index 5c624b914..689c03856 100644 --- a/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx +++ b/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx @@ -15,7 +15,6 @@ import { IMemberType, mapRoleToDisplayName, } from '~/console/routes/_main+/$account+/settings+/user-management/user-access-resource'; -import { useState } from 'react'; import { IAccountContext } from '../../_layout'; type IDialog = IDialogBase; @@ -36,7 +35,6 @@ const Root = (props: IDialog) => { const api = useConsoleApi(); const { account } = useOutletContext(); - const [inputName, setInputName] = useState(''); const { values, handleChange, handleSubmit, isLoading } = useForm({ initialValues: isUpdate @@ -117,23 +115,6 @@ const Root = (props: IDialog) => { })} - {isUpdate && ( -
-
- Type in confirm to continue. -
-
- { - setInputName(target.value); - }} - // disabled={loading} - autoComplete="off" - /> -
-
- )} @@ -142,7 +123,6 @@ const Root = (props: IDialog) => { type="submit" content={isUpdate ? 'update' : 'Send invite'} variant="primary" - disabled={isUpdate ? inputName !== 'confirm' : false} /> From 890719acf8318aac37c5fdf5323eae400fb2e4a3 Mon Sep 17 00:00:00 2001 From: Piyush Kumar Date: Thu, 14 Mar 2024 10:16:27 +0530 Subject: [PATCH 8/8] updated structure --- .../settings+/user-management/handle-user.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx b/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx index 689c03856..b2ebc1e9e 100644 --- a/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx +++ b/src/apps/console/routes/_main+/$account+/settings+/user-management/handle-user.tsx @@ -21,9 +21,9 @@ type IDialog = IDialogBase; const validRoles = (role: string): Role => { switch (role) { - case 'owner': + case 'account_owner': return 'account_owner' as Role; - case 'member': + case 'account_member': return 'account_member' as Role; default: throw new Error(`invalid role ${role}`); @@ -40,11 +40,11 @@ const Root = (props: IDialog) => { initialValues: isUpdate ? { email: props?.data.email || '', - role: mapRoleToDisplayName(props?.data.role) || 'member', + role: props?.data.role || 'account_member', } : { email: '', - role: 'member', + role: 'account_member', }, validationSchema: Yup.object({ email: Yup.string().required().email(), @@ -80,7 +80,7 @@ const Root = (props: IDialog) => { }, }); - const roles: string[] = ['owner', 'member']; + const roles: string[] = ['account_owner', 'account_member']; return ( @@ -109,7 +109,7 @@ const Root = (props: IDialog) => { {roles.map((role) => { return ( - {role} + {mapRoleToDisplayName(role)} ); })}