diff --git a/frontend/src/locale/src/de.json b/frontend/src/locale/src/de.json
index ae247e3..fa56e49 100644
--- a/frontend/src/locale/src/de.json
+++ b/frontend/src/locale/src/de.json
@@ -119,9 +119,6 @@
"create-hint": {
"defaultMessage": "Warum erstellen Sie nicht eine?"
},
- "create-host": {
- "defaultMessage": "Host erstellen"
- },
"create-host-title": {
"defaultMessage": "Es gibt keine Proxy-Hosts"
},
@@ -242,6 +239,9 @@
"general-settings.title": {
"defaultMessage": "Allgemeine Einstellungen"
},
+ "host.create": {
+ "defaultMessage": "Host erstellen"
+ },
"hosts.title": {
"defaultMessage": "Gastgeber"
},
diff --git a/frontend/src/locale/src/en.json b/frontend/src/locale/src/en.json
index 1d6c226..c539c5e 100644
--- a/frontend/src/locale/src/en.json
+++ b/frontend/src/locale/src/en.json
@@ -395,9 +395,6 @@
"create-hint": {
"defaultMessage": "Why don't you create one?"
},
- "create-host": {
- "defaultMessage": "Create Host"
- },
"create-host-title": {
"defaultMessage": "There are no Hosts"
},
@@ -518,6 +515,9 @@
"general-settings.title": {
"defaultMessage": "General Settings"
},
+ "host.create": {
+ "defaultMessage": "Create Host"
+ },
"hosts.title": {
"defaultMessage": "Hosts"
},
diff --git a/frontend/src/locale/src/fa.json b/frontend/src/locale/src/fa.json
index 7922bc1..92d45ef 100644
--- a/frontend/src/locale/src/fa.json
+++ b/frontend/src/locale/src/fa.json
@@ -119,9 +119,6 @@
"create-hint": {
"defaultMessage": "چرا یکی را ایجاد نمی کنید؟"
},
- "create-host": {
- "defaultMessage": "هاست ایجاد کنید"
- },
"create-nginx-template": {
"defaultMessage": "قالب هاست ایجاد کنید"
},
@@ -245,6 +242,9 @@
"nginx-templates.title": {
"defaultMessage": "قالب های میزبان"
},
+ "host.create": {
+ "defaultMessage": "هاست ایجاد کنید"
+ },
"hosts.title": {
"defaultMessage": "میزبان"
},
diff --git a/frontend/src/modals/HostCreateModal.tsx b/frontend/src/modals/HostCreateModal.tsx
new file mode 100644
index 0000000..dad69c5
--- /dev/null
+++ b/frontend/src/modals/HostCreateModal.tsx
@@ -0,0 +1,218 @@
+import {
+ Button,
+ Checkbox,
+ FormControl,
+ FormErrorMessage,
+ FormLabel,
+ Input,
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ ModalCloseButton,
+ ModalBody,
+ ModalFooter,
+ Stack,
+ useToast,
+} from "@chakra-ui/react";
+import { CertificateAuthority } from "api/npm";
+import { PrettyButton } from "components";
+import { Formik, Form, Field } from "formik";
+import { useSetCertificateAuthority } from "hooks";
+import { intl } from "locale";
+import { validateNumber, validateString } from "modules/Validations";
+
+interface HostCreateModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+}
+function HostCreateModal({ isOpen, onClose }: HostCreateModalProps) {
+ const toast = useToast();
+ const { mutate: setCertificateAuthority } = useSetCertificateAuthority();
+
+ const onSubmit = async (
+ payload: CertificateAuthority,
+ { setErrors, setSubmitting }: any,
+ ) => {
+ const showErr = (msg: string) => {
+ toast({
+ description: intl.formatMessage({
+ id: `error.${msg}`,
+ }),
+ status: "error",
+ position: "top",
+ duration: 3000,
+ isClosable: true,
+ });
+ };
+
+ setCertificateAuthority(payload, {
+ onError: (err: any) => {
+ if (err.message === "ca-bundle-does-not-exist") {
+ setErrors({
+ caBundle: intl.formatMessage({
+ id: `error.${err.message}`,
+ }),
+ });
+ } else {
+ showErr(err.message);
+ }
+ },
+ onSuccess: () => onClose(),
+ onSettled: () => setSubmitting(false),
+ });
+ };
+
+ return (
+
+
+
+
+ {({ isSubmitting }) => (
+
+ )}
+
+
+
+ );
+}
+
+export { HostCreateModal };
diff --git a/frontend/src/modals/index.ts b/frontend/src/modals/index.ts
index 31af6ca..dbfecf8 100644
--- a/frontend/src/modals/index.ts
+++ b/frontend/src/modals/index.ts
@@ -6,6 +6,7 @@ export * from "./CertificateEditModal";
export * from "./ChangePasswordModal";
export * from "./DNSProviderCreateModal";
// export * from "./DNSProviderEditModal.tsx.disabled";
+export * from "./HostCreateModal";
export * from "./ProfileModal";
export * from "./SetPasswordModal";
export * from "./UpstreamCreateModal";
diff --git a/frontend/src/pages/Hosts/HostsTable.tsx b/frontend/src/pages/Hosts/Table.tsx
similarity index 89%
rename from frontend/src/pages/Hosts/HostsTable.tsx
rename to frontend/src/pages/Hosts/Table.tsx
index 3c4ed8f..63a0521 100644
--- a/frontend/src/pages/Hosts/HostsTable.tsx
+++ b/frontend/src/pages/Hosts/Table.tsx
@@ -18,33 +18,22 @@ import { intl } from "locale";
import { FiEdit } from "react-icons/fi";
import { useSortBy, useFilters, useTable, usePagination } from "react-table";
-const rowActions = [
- {
- title: intl.formatMessage({ id: "action.edit" }),
- onClick: (e: any, data: any) => {
- alert(JSON.stringify(data, null, 2));
- },
- icon: ,
- show: (data: any) => !data.isSystem,
- },
-];
-
-export interface HostsTableProps {
+export interface TableProps {
data: any;
pagination: TablePagination;
sortBy: TableSortBy[];
filters: TableFilter[];
onTableEvent: any;
}
-function HostsTable({
+function Table({
data,
pagination,
onTableEvent,
sortBy,
filters,
-}: HostsTableProps) {
+}: TableProps) {
const [columns, tableData] = useMemo(() => {
- const columns: any[] = [
+ const columns: any = [
{
accessor: "user.gravatarUrl",
Cell: GravatarFormatter(),
@@ -82,7 +71,15 @@ function HostsTable({
{
id: "actions",
accessor: "id",
- Cell: ActionsFormatter(rowActions),
+ Cell: ActionsFormatter([
+ {
+ title: intl.formatMessage({ id: "action.edit" }),
+ onClick: (e: any, data: any) => {
+ alert(JSON.stringify(data, null, 2));
+ },
+ icon: ,
+ },
+ ]),
className: "w-80",
},
];
@@ -162,4 +159,4 @@ function HostsTable({
return ;
}
-export { HostsTable };
+export default Table;
diff --git a/frontend/src/pages/Hosts/TableWrapper.tsx b/frontend/src/pages/Hosts/TableWrapper.tsx
new file mode 100644
index 0000000..c712b62
--- /dev/null
+++ b/frontend/src/pages/Hosts/TableWrapper.tsx
@@ -0,0 +1,97 @@
+import { useEffect, useReducer, useState } from "react";
+
+import { Alert, AlertIcon } from "@chakra-ui/react";
+import {
+ EmptyList,
+ PrettyButton,
+ SpinnerPage,
+ tableEventReducer,
+} from "components";
+import { useHosts } from "hooks";
+import { intl } from "locale";
+
+import Table from "./Table";
+
+const initialState = {
+ offset: 0,
+ limit: 10,
+ sortBy: [
+ {
+ id: "name",
+ desc: false,
+ },
+ ],
+ filters: [],
+};
+
+interface TableWrapperProps {
+ onCreateClick?: () => void;
+}
+function TableWrapper({ onCreateClick }: TableWrapperProps) {
+ const [{ offset, limit, sortBy, filters }, dispatch] = useReducer(
+ tableEventReducer,
+ initialState,
+ );
+
+ const [tableData, setTableData] = useState(null);
+ const { isFetching, isLoading, isError, error, data } = useHosts(
+ offset,
+ limit,
+ sortBy,
+ filters,
+ );
+
+ useEffect(() => {
+ setTableData(data as any);
+ }, [data]);
+
+ if (isFetching || isLoading || !tableData) {
+ return ;
+ }
+
+ if (isError) {
+ return (
+
+
+ {error?.message || "Unknown error"}
+
+ );
+ }
+
+ if (isFetching || isLoading || !tableData) {
+ return ;
+ }
+
+ // When there are no items and no filters active, show the nicer empty view
+ if (data?.total === 0 && filters?.length === 0) {
+ return (
+
+ {intl.formatMessage({ id: "lets-go" })}
+
+ }
+ />
+ );
+ }
+
+ const pagination = {
+ offset: data?.offset || initialState.offset,
+ limit: data?.limit || initialState.limit,
+ total: data?.total || 0,
+ };
+
+ return (
+
+ );
+}
+
+export default TableWrapper;
diff --git a/frontend/src/pages/Hosts/index.tsx b/frontend/src/pages/Hosts/index.tsx
index 65e4b2c..6a02e8c 100644
--- a/frontend/src/pages/Hosts/index.tsx
+++ b/frontend/src/pages/Hosts/index.tsx
@@ -1,95 +1,30 @@
-import { useEffect, useReducer, useState } from "react";
+import { useState } from "react";
-import { Alert, AlertIcon, Heading, HStack } from "@chakra-ui/react";
-import {
- EmptyList,
- PrettyButton,
- SpinnerPage,
- tableEventReducer,
-} from "components";
-import { useHosts } from "hooks";
+import { Heading, HStack } from "@chakra-ui/react";
+import { HelpDrawer, PrettyButton } from "components";
import { intl } from "locale";
+import { HostCreateModal } from "modals";
-import { HostsTable } from "./HostsTable";
-
-const initialState = {
- offset: 0,
- limit: 10,
- sortBy: [
- {
- id: "domain_names",
- desc: false,
- },
- ],
- filters: [],
-};
+import TableWrapper from "./TableWrapper";
function Hosts() {
- const [{ offset, limit, sortBy, filters }, dispatch] = useReducer(
- tableEventReducer,
- initialState,
- );
-
- const [tableData, setTableData] = useState(null);
- const { isFetching, isLoading, error, data } = useHosts(
- offset,
- limit,
- sortBy,
- filters,
- );
-
- useEffect(() => {
- setTableData(data as any);
- }, [data]);
-
- if (error || (!tableData && !isFetching && !isLoading)) {
- return (
-
-
- {error?.message || "Unknown error"}
-
- );
- }
-
- if (isFetching || isLoading || !tableData) {
- return ;
- }
-
- // When there are no items and no filters active, show the nicer empty view
- if (data?.total === 0 && filters?.length === 0) {
- return (
-
- {intl.formatMessage({ id: "lets-go" })}
-
- }
- />
- );
- }
-
- const pagination = {
- offset: data?.offset || initialState.offset,
- limit: data?.limit || initialState.limit,
- total: data?.total || 0,
- };
+ const [createShown, setCreateShown] = useState(false);
return (
<>
{intl.formatMessage({ id: "hosts.title" })}
-
- {intl.formatMessage({ id: "create-host" })}
-
+
+
+ setCreateShown(true)}>
+ {intl.formatMessage({ id: "host.create" })}
+
+
- setCreateShown(true)} />
+ setCreateShown(false)}
/>
>
);