diff --git a/src/apps/console/components/console-list-components.tsx b/src/apps/console/components/console-list-components.tsx
index 726ca636d..9da28f165 100644
--- a/src/apps/console/components/console-list-components.tsx
+++ b/src/apps/console/components/console-list-components.tsx
@@ -1,9 +1,6 @@
import {ReactNode, useState} from 'react';
import Tooltip from '~/components/atoms/tooltip';
-import {cn, titleCase} from '~/components/utils';
-import {CopyrightFill, CopySimple} from "@jengaicons/react";
-import useClipboard from "~/lib/client/hooks/use-clipboard";
-import {toast} from "~/components/molecule/toast";
+import {cn} from '~/components/utils';
interface IBase {
className?: string;
@@ -134,41 +131,27 @@ const ListTitle = ({
};
const ListDomainItem = ({
- data,
- value,
- }: {
+ data,
+ value,
+ }: {
data: ReactNode;
value: string;
}) => {
- const [_, setCopyIcon] = useState();
- const {copy} = useClipboard({
- onSuccess: () => {
- setTimeout(() => {
- setCopyIcon();
- toast.success(`${titleCase("domain name")} copied successfully`);
- }, 1000);
- // toast.success('Copied to clipboard');
- },
- });
-
return (
{
event.preventDefault()
- copy(value);
+ window.open(`https://${value}`, "_blank")
}}
- className="flex flex-row gap-md items-center select-none group cursor-pointer"
+ className="flex flex-row gap-md items-center"
>
);
};
diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/routes/handle-route.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/routes/handle-route.tsx
index 14fd00d13..78dbcdb47 100644
--- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/routes/handle-route.tsx
+++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/routes/handle-route.tsx
@@ -1,253 +1,276 @@
/* eslint-disable react/destructuring-assignment */
-import { useParams } from '@remix-run/react';
+import {useParams} from '@remix-run/react';
import Popup from '~/components/molecule/popup';
-import { toast } from '~/components/molecule/toast';
+import {toast} from '~/components/molecule/toast';
import {
- ExtractNodeType,
- parseName,
- parseNodes,
+ ExtractNodeType,
+ parseName,
+ parseNodes,
} from '~/console/server/r-utils/common';
-import { useReload } from '~/root/lib/client/helpers/reloader';
-import useForm, { dummyEvent } from '~/root/lib/client/hooks/use-form';
+import {useReload} from '~/root/lib/client/helpers/reloader';
+import useForm, {dummyEvent} from '~/root/lib/client/hooks/use-form';
import Yup from '~/root/lib/server/helpers/yup';
-import { handleError } from '~/root/lib/utils/common';
-import { IDialogBase } from '~/console/components/types.d';
-import { IRouters } from '~/console/server/gql/queries/router-queries';
-import { useConsoleApi } from '~/console/server/gql/api-provider';
+import {handleError} from '~/root/lib/utils/common';
+import {IDialogBase} from '~/console/components/types.d';
+import {IRouters} from '~/console/server/gql/queries/router-queries';
+import {useConsoleApi} from '~/console/server/gql/api-provider';
import Select from '~/components/atoms/select';
import useCustomSwr from '~/root/lib/client/hooks/use-custom-swr';
-import { useMapper } from '~/components/utils';
-import { NN } from '~/root/lib/types/common';
-import { TextInput } from '~/components/atoms/input';
-import { useEffect, useState } from 'react';
-import { IApps } from '~/console/server/gql/queries/app-queries';
-import { ModifiedRouter } from './_index';
+import {useMapper} from '~/components/utils';
+import {NN} from '~/root/lib/types/common';
+import {TextInput} from '~/components/atoms/input';
+import {useEffect, useState} from 'react';
+import {IApps} from '~/console/server/gql/queries/app-queries';
+import {ModifiedRouter} from './_index';
+import {Switch} from "~/components/atoms/switch";
type IDialog = IDialogBase<
- NN
['spec']['routes']>[number] & { id: string }
+ NN['spec']['routes']>[number] & { id: string }
> & { router?: ModifiedRouter };
const Root = (props: IDialog) => {
- const { isUpdate, setVisible } = props;
- const api = useConsoleApi();
- const reloadPage = useReload();
+ const {isUpdate, setVisible} = props;
+ const api = useConsoleApi();
+ const reloadPage = useReload();
- const { project: projectName, environment: envName } = useParams();
- const [selectedApp, setSelectedApp] = useState>();
+ const {project: projectName, environment: envName} = useParams();
+ const [selectedApp, setSelectedApp] = useState>();
- const {
- data,
- isLoading: appLoading,
- error: appLoadingError,
- } = useCustomSwr('/apps', async () => {
- if (!projectName || !envName) {
- throw new Error('Project and Environment is required!.');
- }
- return api.listApps({ projectName, envName });
- });
-
- const { values, errors, handleSubmit, handleChange, isLoading, resetValues } =
- useForm({
- initialValues: isUpdate
- ? {
- path:
- props.data.path?.[0] === '/'
- ? props.data.path.substring(1)
- : props.data.path,
- app: props.data.app || '',
- port: `${props.data.port}`,
- }
- : {
- path: '',
- app: '',
- port: '',
- },
- validationSchema: Yup.object({
- path: Yup.string().test(
- 'is-valid',
- 'Path should not contain spaces.',
- (value) => {
- return !value?.includes(' ');
- }
- ),
- app: Yup.string().required(),
- port: Yup.string().required(),
- }),
-
- onSubmit: async (val) => {
- const { router } = props;
- if (!projectName || !envName || !router || !router.metadata?.name) {
- throw new Error('Project, Router and Environment is required!.');
+ const {
+ data,
+ isLoading: appLoading,
+ error: appLoadingError,
+ } = useCustomSwr('/apps', async () => {
+ if (!projectName || !envName) {
+ throw new Error('Project and Environment is required!.');
}
- try {
- if (!isUpdate) {
- const { errors: e } = await api.updateRouter({
- envName,
- projectName,
- router: {
- displayName: router.displayName,
- spec: {
- ...router.spec,
- routes: [
- ...(router.spec.routes?.map((r) => ({
- path: r.path,
- app: r.app,
- port: r.port,
- })) || []),
- {
- path: `/${val.path}`,
- app: val.app,
- port: parseInt(val.port, 10),
- },
- ],
- },
- metadata: {
- ...router.metadata,
- },
- },
- });
- if (e) {
- throw e[0];
- }
- toast.success('Route created successfully');
- } else {
- const { errors: e } = await api.updateRouter({
- envName,
- projectName,
- router: {
- displayName: router.displayName,
- spec: {
- ...router.spec,
- routes: [
- ...(router.spec.routes
+ return api.listApps({projectName, envName});
+ });
- ?.filter(
- (
- rou // @ts-ignore
- ) => rou.id !== props.data.id
- )
- .map((route) => ({
- app: route.app,
- path: route.path,
- port: route.port,
- })) || []),
- {
- path: val.path,
- app: val.app,
- port: parseInt(val.port, 10),
- },
- ],
- },
- metadata: {
- ...router.metadata,
+ const {values, errors, handleSubmit, handleChange, isLoading, resetValues} =
+ useForm({
+ initialValues: isUpdate
+ ? {
+ path:
+ props.data.path?.[0] === '/'
+ ? props.data.path.substring(1)
+ : props.data.path,
+ app: props.data.app || '',
+ port: `${props.data.port}`,
+ reWrite: false,
+ }
+ : {
+ path: '',
+ app: '',
+ port: '',
+ reWrite: false
},
- },
- });
- if (e) {
- throw e[0];
+ validationSchema: Yup.object({
+ path: Yup.string().test(
+ 'is-valid',
+ 'Path should not contain spaces.',
+ (value) => {
+ return !value?.includes(' ');
+ }
+ ),
+ app: Yup.string().required(),
+ port: Yup.string().required(),
+ }),
+
+ onSubmit: async (val) => {
+ const {router} = props;
+ if (!projectName || !envName || !router || !router.metadata?.name) {
+ throw new Error('Project, Router and Environment is required!.');
+ }
+ try {
+ if (!isUpdate) {
+ const {errors: e} = await api.updateRouter({
+ envName,
+ projectName,
+ router: {
+ displayName: router.displayName,
+ spec: {
+ ...router.spec,
+ routes: [
+ ...(router.spec.routes?.map((r) => ({
+ path: r.path,
+ app: r.app,
+ port: r.port,
+ })) || []),
+ {
+ path: `/${val.path}`,
+ app: val.app,
+ port: parseInt(val.port, 10),
+ },
+ ],
+ },
+ metadata: {
+ ...router.metadata,
+ },
+ },
+ });
+ if (e) {
+ throw e[0];
+ }
+ toast.success('Route created successfully');
+ } else {
+ const {errors: e} = await api.updateRouter({
+ envName,
+ projectName,
+ router: {
+ displayName: router.displayName,
+ spec: {
+ ...router.spec,
+ routes: [
+ ...(router.spec.routes
+
+ ?.filter(
+ (
+ rou // @ts-ignore
+ ) => rou.id !== props.data.id
+ )
+ .map((route) => ({
+ app: route.app,
+ path: route.path,
+ port: route.port,
+ })) || []),
+ {
+ path: `/${val.path}`,
+ app: val.app,
+ port: parseInt(val.port, 10),
+ },
+ ],
+ },
+ metadata: {
+ ...router.metadata,
+ },
+ },
+ });
+ if (e) {
+ throw e[0];
+ }
+ toast.success('Route updated successfully');
+ }
+ reloadPage();
+ setVisible(false);
+ resetValues();
+ } catch (err) {
+ handleError(err);
+ }
+ },
+ });
+
+ const apps = useMapper(parseNodes(data), (val) => ({
+ label: val.displayName,
+ value: parseName(val),
+ app: val,
+ render: () => val.displayName,
+ }));
+
+ useEffect(() => {
+ const d = parseNodes(data);
+ if (d.length > 0) {
+ if (isUpdate) {
+ setSelectedApp(d.find((app) => parseName(app) === props.data.app));
+ } else if (d.length === 1) {
+ handleChange('app')(dummyEvent(parseName(d[0])));
+ setSelectedApp(d[0]);
}
- toast.success('Route updated successfully');
- }
- reloadPage();
- setVisible(false);
- resetValues();
- } catch (err) {
- handleError(err);
}
- },
- });
+ }, [isUpdate, data]);
- const apps = useMapper(parseNodes(data), (val) => ({
- label: val.displayName,
- value: parseName(val),
- app: val,
- render: () => val.displayName,
- }));
+ useEffect(() => {
+ if (selectedApp?.spec.services?.length === 0) {
+ handleChange('port')(dummyEvent(selectedApp.spec.services[0].port));
+ }
+ }, [selectedApp]);
- useEffect(() => {
- const d = parseNodes(data);
- if (d.length > 0) {
- if (isUpdate) {
- setSelectedApp(d.find((app) => parseName(app) === props.data.app));
- } else if (d.length === 1) {
- handleChange('app')(dummyEvent(parseName(d[0])));
- setSelectedApp(d[0]);
- }
- }
- }, [isUpdate, data]);
+ return (
+
+
- useEffect(() => {
- if (selectedApp?.spec.services?.length === 0) {
- handleChange('port')(dummyEvent(selectedApp.spec.services[0].port));
- }
- }, [selectedApp]);
+
+
+
+ {
+ handleChange('path')(dummyEvent(e.target.value.toLowerCase()));
+ }}
+ error={!!errors.path}
+ message={errors.path}
+ prefix="/"
+ />
+
+
- return (
-
-
- {
- handleChange('path')(dummyEvent(e.target.value.toLowerCase()));
- }}
- error={!!errors.path}
- message={errors.path}
- prefix="/"
- />
-
-
-
-
-
-
- );
+
+
Rewrite
+
+ {
+ handleChange('reWrite')(dummyEvent(val));
+ }}
+ />
+
+
+
+ [...apps]}
+ onChange={(val) => {
+ handleChange('app')(dummyEvent(val.value));
+ setSelectedApp(val.app);
+ }}
+ error={!!errors.app || !!appLoadingError}
+ message={appLoadingError ? 'Error fetching apps.' : errors.app}
+ loading={appLoading}
+ />
+ [
+ ...(selectedApp?.spec.services?.map((svc) => ({
+ label: `${svc.port}`,
+ value: `${svc.port}`,
+ })) || []),
+ ]}
+ onChange={(val) => {
+ handleChange('port')(dummyEvent(val.value));
+ }}
+ error={!!errors.port}
+ message={errors.port}
+ />
+
+
+
+
+
+
+ );
};
const HandleRoute = (props: IDialog) => {
- const { isUpdate, setVisible, visible } = props;
- return (
- setVisible(v)}>
- {isUpdate ? 'Edit route' : 'Add Route'}
- {(!isUpdate || (isUpdate && props.data)) && }
-
- );
+ const {isUpdate, setVisible, visible} = props;
+ return (
+ setVisible(v)}>
+ {isUpdate ? 'Edit route' : 'Add Route'}
+ {(!isUpdate || (isUpdate && props.data)) && }
+
+ );
};
export default HandleRoute;
diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/router-resources.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/router-resources.tsx
index a0aad3b01..872ebf7dc 100644
--- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/router-resources.tsx
+++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/router-resources.tsx
@@ -1,11 +1,11 @@
-import { Trash, PencilLine } from '@jengaicons/react';
-import { useState } from 'react';
-import { generateKey, titleCase } from '~/components/utils';
+import {Trash, PencilLine} from '@jengaicons/react';
+import {useState} from 'react';
+import {generateKey, titleCase} from '~/components/utils';
import {
- ListItem,
- ListTitle,
- listClass,
- listFlex, ListDomainItem,
+ ListItem,
+ ListTitle,
+ listClass,
+ listFlex, ListDomainItem,
} from '~/console/components/console-list-components';
import DeleteDialog from '~/console/components/delete-dialog';
import Grid from '~/console/components/grid';
@@ -13,18 +13,18 @@ import List from '~/console/components/list';
import ListGridView from '~/console/components/list-grid-view';
import ResourceExtraAction from '~/console/components/resource-extra-action';
import {
- ExtractNodeType,
- parseName,
- parseUpdateOrCreatedBy,
- parseUpdateOrCreatedOn,
+ ExtractNodeType,
+ parseName,
+ parseUpdateOrCreatedBy,
+ parseUpdateOrCreatedOn,
} from '~/console/server/r-utils/common';
-import { handleError } from '~/root/lib/utils/common';
-import { IRouters } from '~/console/server/gql/queries/router-queries';
-import { Link, useParams } from '@remix-run/react';
-import { listStatus } from '~/console/components/sync-status';
-import { useConsoleApi } from '~/console/server/gql/api-provider';
-import { useReload } from '~/root/lib/client/helpers/reloader';
-import { toast } from '~/components/molecule/toast';
+import {handleError} from '~/root/lib/utils/common';
+import {IRouters} from '~/console/server/gql/queries/router-queries';
+import {Link, useParams} from '@remix-run/react';
+import {listStatus} from '~/console/components/sync-status';
+import {useConsoleApi} from '~/console/server/gql/api-provider';
+import {useReload} from '~/root/lib/client/helpers/reloader';
+import {toast} from '~/components/molecule/toast';
import HandleRouter from './handle-router';
import {CopyButton} from "~/console/components/commons";
@@ -32,234 +32,271 @@ const RESOURCE_NAME = 'domain';
type BaseType = ExtractNodeType;
const parseItem = (item: BaseType) => {
- return {
- name: item.displayName,
- id: parseName(item),
- updateInfo: {
- author: `Updated by ${parseUpdateOrCreatedBy(item)}`,
- time: parseUpdateOrCreatedOn(item),
- },
- };
+ return {
+ name: item.displayName,
+ id: parseName(item),
+ updateInfo: {
+ author: `Updated by ${parseUpdateOrCreatedBy(item)}`,
+ time: parseUpdateOrCreatedOn(item),
+ },
+ };
};
type OnAction = ({
- action,
- item,
-}: {
- action: 'edit' | 'delete' | 'detail';
- item: BaseType;
+ action,
+ item,
+ }: {
+ action: 'edit' | 'delete' | 'detail';
+ item: BaseType;
}) => void;
type IExtraButton = {
- onAction: OnAction;
- item: BaseType;
+ onAction: OnAction;
+ item: BaseType;
};
-const ExtraButton = ({ onAction, item }: IExtraButton) => {
- return (
- ,
- type: 'item',
- onClick: () => onAction({ action: 'edit', item }),
- key: 'edit',
- },
- {
- label: 'Delete',
- icon: ,
- type: 'item',
- onClick: () => onAction({ action: 'delete', item }),
- key: 'delete',
- className: '!text-text-critical',
- },
- ]}
- />
- );
+const ExtraButton = ({onAction, item}: IExtraButton) => {
+ return (
+ ,
+ type: 'item',
+ onClick: () => onAction({action: 'edit', item}),
+ key: 'edit',
+ },
+ {
+ label: 'Delete',
+ icon: ,
+ type: 'item',
+ onClick: () => onAction({action: 'delete', item}),
+ key: 'delete',
+ className: '!text-text-critical',
+ },
+ ]}
+ />
+ );
};
interface IResource {
- items: BaseType[];
- onAction: OnAction;
+ items: BaseType[];
+ onAction: OnAction;
}
-const GridView = ({ items, onAction }: IResource) => {
- const { account, project, environment } = useParams();
- return (
-
- {items.map((item, index) => {
- const { name, id, updateInfo } = parseItem(item);
- const keyPrefix = `${RESOURCE_NAME}-${id}-${index}`;
- return (
- (
- }
- />
- ),
- },
- {
- key: generateKey(keyPrefix, name),
- className: listClass.author,
- render: () => (
- item.spec.domains.map((domain) => {
- return (
-
- )
- })
- ),
- },
- {
- key: generateKey(keyPrefix, updateInfo.author),
- render: () => (
-
- ),
- },
- ]}
- />
- );
- })}
-
- );
+const GridView = ({items, onAction}: IResource) => {
+ const {account, project, environment} = useParams();
+ return (
+
+ {items.map((item, index) => {
+ const {name, id, updateInfo} = parseItem(item);
+ const keyPrefix = `${RESOURCE_NAME}-${id}-${index}`;
+ const [extraDomain, setExtraDomain] = useState(false)
+ return (
+ (
+ }
+ />
+ ),
+ },
+ {
+ key: generateKey(keyPrefix, name),
+ render: () => (
+ !extraDomain ?
+
+
+ {item.spec.domains.length > 1 && (
+
{
+ event.preventDefault()
+ setExtraDomain(true)
+ }}>
+ +{item.spec.domains.length - 1} domains
+
+ )}
+
+ :
+ item.spec.domains.map((domain) => {
+ return (
+
+ )
+ })
+ ),
+ },
+ {
+ key: generateKey(keyPrefix, updateInfo.author),
+ render: () => (
+
+ ),
+ },
+ ]}
+ />
+ );
+ })}
+
+ );
};
-const ListView = ({ items, onAction }: IResource) => {
- const { account, project, environment } = useParams();
- return (
-
- {items.map((item, index) => {
- const { name, id, updateInfo } = parseItem(item);
- const keyPrefix = `${RESOURCE_NAME}-${id}-${index}`;
- const status = listStatus({ key: `${keyPrefix}status`, item });
- return (
- ,
- },
- status,
- {
- key: generateKey(keyPrefix, name),
- render: () => (
- item.spec.domains.map((domain) => {
- return (
-
- )
- })
- ),
- },
- listFlex({ key: 'flex-1' }),
- {
- key: generateKey(keyPrefix, updateInfo.author),
- className: listClass.author,
- render: () => (
-
- ),
- },
- {
- key: generateKey(keyPrefix, 'action'),
- render: () => ,
- },
- ]}
- />
- );
- })}
-
- );
+const ListView = ({items, onAction}: IResource) => {
+ const {account, project, environment} = useParams();
+ return (
+
+ {items.map((item, index) => {
+ const {name, id, updateInfo} = parseItem(item);
+ const keyPrefix = `${RESOURCE_NAME}-${id}-${index}`;
+ const status = listStatus({key: `${keyPrefix}status`, item});
+ const [extraDomain, setExtraDomain] = useState(false)
+ return (
+ ,
+ },
+ status,
+ {
+ key: generateKey(keyPrefix, name),
+ render: () => (
+ !extraDomain ?
+
+
+ {item.spec.domains.length > 1 && (
+
{
+ event.preventDefault()
+ setExtraDomain(true)
+ }}>
+ +{item.spec.domains.length - 1} domains
+
+ )}
+
+ :
+ item.spec.domains.map((domain) => {
+ return (
+
+ )
+ })
+ ),
+ },
+ listFlex({key: 'flex-1'}),
+ {
+ key: generateKey(keyPrefix, updateInfo.author),
+ className: listClass.author,
+ render: () => (
+
+ ),
+ },
+ {
+ key: generateKey(keyPrefix, 'action'),
+ render: () => ,
+ },
+ ]}
+ />
+ );
+ })}
+
+ );
};
-const RouterResources = ({ items = [] }: { items: BaseType[] }) => {
- const [showDeleteDialog, setShowDeleteDialog] = useState(
- null
- );
- const [visible, setVisible] = useState(null);
- const api = useConsoleApi();
- const reloadPage = useReload();
- const { environment, project } = useParams();
-
- const props: IResource = {
- items,
- onAction: ({ action, item }) => {
- switch (action) {
- case 'edit':
- setVisible(item);
- break;
- case 'delete':
- setShowDeleteDialog(item);
- break;
- default:
- }
- },
- };
- return (
- <>
- }
- gridView={}
- />
- {
- if (!environment || !project) {
- throw new Error('Project and Environment is required!.');
- }
- try {
- const { errors } = await api.deleteRouter({
- envName: environment,
- projectName: project,
- routerName: parseName(showDeleteDialog),
- });
+const RouterResources = ({items = []}: { items: BaseType[] }) => {
+ const [showDeleteDialog, setShowDeleteDialog] = useState(
+ null
+ );
+ const [visible, setVisible] = useState(null);
+ const api = useConsoleApi();
+ const reloadPage = useReload();
+ const {environment, project} = useParams();
- if (errors) {
- throw errors[0];
+ const props: IResource = {
+ items,
+ onAction: ({action, item}) => {
+ switch (action) {
+ case 'edit':
+ setVisible(item);
+ break;
+ case 'delete':
+ setShowDeleteDialog(item);
+ break;
+ default:
}
- reloadPage();
- toast.success(`${titleCase(RESOURCE_NAME)} deleted successfully`);
- setShowDeleteDialog(null);
- } catch (err) {
- handleError(err);
- }
- }}
- />
- setVisible(null),
- }}
- />
- >
- );
+ },
+ };
+ return (
+ <>
+ }
+ gridView={}
+ />
+ {
+ if (!environment || !project) {
+ throw new Error('Project and Environment is required!.');
+ }
+ try {
+ const {errors} = await api.deleteRouter({
+ envName: environment,
+ projectName: project,
+ routerName: parseName(showDeleteDialog),
+ });
+
+ if (errors) {
+ throw errors[0];
+ }
+ reloadPage();
+ toast.success(`${titleCase(RESOURCE_NAME)} deleted successfully`);
+ setShowDeleteDialog(null);
+ } catch (err) {
+ handleError(err);
+ }
+ }}
+ />
+ setVisible(null),
+ }}
+ />
+ >
+ );
};
export default RouterResources;