diff --git a/messages/en-US.json b/messages/en-US.json index df2d9799..a9051087 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -59,7 +59,6 @@ "siteErrorCreate": "Error creating site", "siteErrorCreateKeyPair": "Key pair or site defaults not found", "siteErrorCreateDefaults": "Site defaults not found", - "siteNameDescription": "This is the display name for the site.", "method": "Method", "siteMethodDescription": "This is how you will expose connections.", "siteLearnNewt": "Learn how to install Newt on your system", @@ -1291,7 +1290,6 @@ "seeAllClients": "See All Clients", "clientInformation": "Client Information", "clientNamePlaceholder": "Client name", - "clientNameDescription": "A friendly name for this client", "address": "Address", "subnetPlaceholder": "Subnet", "addressDescription": "The address that this client will use for connectivity", @@ -1305,5 +1303,14 @@ "olmId": "Olm ID", "olmSecretKey": "Olm Secret Key", "clientCredentialsSave": "Save Your Credentials", - "clientCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place." + "clientCredentialsSaveDescription": "You will only be able to see this once. Make sure to copy it to a secure place.", + "generalSettingsDescription": "Configure the general settings for this client", + "clientUpdated": "Client updated", + "clientUpdatedDescription": "The client has been updated.", + "clientUpdateFailed": "Failed to update client", + "clientUpdateError": "An error occurred while updating the client.", + "sitesFetchFailed": "Failed to fetch sites", + "sitesFetchError": "An error occurred while fetching sites.", + "olmErrorFetchReleases": "An error occurred while fetching Olm releases.", + "olmErrorFetchLatest": "An error occurred while fetching the latest Olm release." } \ No newline at end of file diff --git a/src/app/[orgId]/settings/clients/ClientsTable.tsx b/src/app/[orgId]/settings/clients/ClientsTable.tsx index 90f04ca8..89766dfc 100644 --- a/src/app/[orgId]/settings/clients/ClientsTable.tsx +++ b/src/app/[orgId]/settings/clients/ClientsTable.tsx @@ -25,7 +25,6 @@ import { toast } from "@app/hooks/useToast"; import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; -import CreateClientFormModal from "./CreateClientsModal"; export type ClientRow = { id: number; @@ -250,15 +249,6 @@ export default function ClientsTable({ clients, orgId }: ClientTableProps) { return ( <> - { - setRows([val, ...rows]); - }} - orgId={orgId} - /> - {selectedClient && ( val.length > 0, { - message: "At least one site is required." - }), - subnet: z.string().min(1, { - message: "Subnet is required." - }) -}); - -type CreateClientFormValues = z.infer; - -const defaultValues: Partial = { - name: "", - siteIds: [], - subnet: "" -}; - -type CreateClientFormProps = { - onCreate?: (client: ClientRow) => void; - setLoading?: (loading: boolean) => void; - setChecked?: (checked: boolean) => void; - orgId: string; -}; - -export default function CreateClientForm({ - onCreate, - setLoading, - setChecked, - orgId -}: CreateClientFormProps) { - const api = createApiClient(useEnvContext()); - const { env } = useEnvContext(); - - const [sites, setSites] = useState([]); - const [isLoading, setIsLoading] = useState(false); - const [isChecked, setIsChecked] = useState(false); - const [clientDefaults, setClientDefaults] = - useState(null); - const [olmCommand, setOlmCommand] = useState(null); - const [selectedSites, setSelectedSites] = useState< - Array<{ id: number; name: string }> - >([]); - const [activeSitesTagIndex, setActiveSitesTagIndex] = useState< - number | null - >(null); - - const handleCheckboxChange = (checked: boolean) => { - setIsChecked(checked); - if (setChecked) { - setChecked(checked); - } - }; - - const form = useForm({ - resolver: zodResolver(createClientFormSchema), - defaultValues - }); - - useEffect(() => { - if (!open) return; - - // reset all values - setLoading?.(false); - setIsLoading(false); - form.reset(); - setChecked?.(false); - setClientDefaults(null); - setSelectedSites([]); - - const fetchSites = async () => { - const res = await api.get>( - `/org/${orgId}/sites/` - ); - const sites = res.data.data.sites.filter( - (s) => s.type === "newt" && s.subnet - ); - setSites( - sites.map((site) => ({ - id: site.siteId.toString(), - text: site.name - })) - ); - }; - - const fetchDefaults = async () => { - api.get(`/org/${orgId}/pick-client-defaults`) - .catch((e) => { - toast({ - variant: "destructive", - title: `Error fetching client defaults`, - description: formatAxiosError(e) - }); - }) - .then((res) => { - if (res && res.status === 200) { - const data = res.data.data; - setClientDefaults(data); - const olmConfig = `olm --id ${data?.olmId} --secret ${data?.olmSecret} --endpoint ${env.app.dashboardUrl}`; - setOlmCommand(olmConfig); - - // Set the subnet value from client defaults - if (data?.subnet) { - form.setValue("subnet", data.subnet); - } - } - }); - }; - fetchSites(); - fetchDefaults(); - }, [open]); - - async function onSubmit(data: CreateClientFormValues) { - setLoading?.(true); - setIsLoading(true); - - if (!clientDefaults) { - toast({ - variant: "destructive", - title: "Error creating client", - description: "Client defaults not found" - }); - setLoading?.(false); - setIsLoading(false); - return; - } - - const payload = { - name: data.name, - siteIds: data.siteIds.map((site) => parseInt(site.id)), - olmId: clientDefaults.olmId, - secret: clientDefaults.olmSecret, - subnet: data.subnet, - type: "olm" - } as CreateClientBody; - - const res = await api - .put< - AxiosResponse - >(`/org/${orgId}/client`, payload) - .catch((e) => { - toast({ - variant: "destructive", - title: "Error creating client", - description: formatAxiosError(e) - }); - }); - - if (res && res.status === 201) { - const data = res.data.data; - - onCreate?.({ - name: data.name, - id: data.clientId, - subnet: data.subnet, - mbIn: "0 MB", - mbOut: "0 MB", - orgId: orgId as string, - online: false - }); - } - - setLoading?.(false); - setIsLoading(false); - } - - return ( -
-
- - ( - - Name - - - - - - )} - /> - - ( - - Address - - - - - The address that this client will use for - connectivity. - - - - )} - /> - - ( - - Sites - { - form.setValue( - "siteIds", - newTags as [Tag, ...Tag[]] - ); - }} - enableAutocomplete={true} - autocompleteOptions={sites} - allowDuplicates={false} - restrictTagsToAutocompleteOptions={true} - sortTags={true} - /> - - The client will have connectivity to the - selected sites. The sites must be configured - to accept client connections. - - - - )} - /> - - {olmCommand && ( -
-
-
- -
-
- - You will only be able to see the configuration - once. - -
- )} - -
- - -
- - -
- ); -} diff --git a/src/app/[orgId]/settings/clients/CreateClientsModal.tsx b/src/app/[orgId]/settings/clients/CreateClientsModal.tsx deleted file mode 100644 index a8921cb1..00000000 --- a/src/app/[orgId]/settings/clients/CreateClientsModal.tsx +++ /dev/null @@ -1,80 +0,0 @@ -"use client"; - -import { Button } from "@app/components/ui/button"; -import { useState } from "react"; -import { - Credenza, - CredenzaBody, - CredenzaClose, - CredenzaContent, - CredenzaDescription, - CredenzaFooter, - CredenzaHeader, - CredenzaTitle -} from "@app/components/Credenza"; -import CreateClientForm from "./CreateClientsForm"; -import { ClientRow } from "./ClientsTable"; - -type CreateClientFormProps = { - open: boolean; - setOpen: (open: boolean) => void; - onCreate?: (client: ClientRow) => void; - orgId: string; -}; - -export default function CreateClientFormModal({ - open, - setOpen, - onCreate, - orgId -}: CreateClientFormProps) { - const [loading, setLoading] = useState(false); - const [isChecked, setIsChecked] = useState(false); - - return ( - <> - { - setOpen(val); - setLoading(false); - }} - > - - - Create Client - - Create a new client to connect to your sites - - - -
- setLoading(val)} - setChecked={(val) => setIsChecked(val)} - onCreate={onCreate} - orgId={orgId} - /> -
-
- - - - - - -
-
- - ); -} diff --git a/src/app/[orgId]/settings/clients/[clientId]/ClientInfoCard.tsx b/src/app/[orgId]/settings/clients/[clientId]/ClientInfoCard.tsx index 7117b4d5..ec8ecacf 100644 --- a/src/app/[orgId]/settings/clients/[clientId]/ClientInfoCard.tsx +++ b/src/app/[orgId]/settings/clients/[clientId]/ClientInfoCard.tsx @@ -9,38 +9,40 @@ import { InfoSections, InfoSectionTitle } from "@app/components/InfoSection"; +import { useTranslations } from "next-intl"; type ClientInfoCardProps = {}; export default function SiteInfoCard({}: ClientInfoCardProps) { const { client, updateClient } = useClientContext(); + const t = useTranslations(); return ( - Client Information + {t("clientInformation")} <> - Status + {t("status")} {client.online ? (
- Online + {t("online")}
) : (
- Offline + {t("offline")}
)}
- Address + {t("address")} {client.subnet.split("/")[0]} diff --git a/src/app/[orgId]/settings/clients/[clientId]/general/page.tsx b/src/app/[orgId]/settings/clients/[clientId]/general/page.tsx index e02e3aaa..27d708a4 100644 --- a/src/app/[orgId]/settings/clients/[clientId]/general/page.tsx +++ b/src/app/[orgId]/settings/clients/[clientId]/general/page.tsx @@ -34,6 +34,7 @@ import { useEffect, useState } from "react"; import { Tag, TagInput } from "@app/components/tags/tag-input"; import { AxiosResponse } from "axios"; import { ListSitesResponse } from "@server/routers/site"; +import { useTranslations } from "next-intl"; const GeneralFormSchema = z.object({ name: z.string().nonempty("Name is required"), @@ -48,6 +49,7 @@ const GeneralFormSchema = z.object({ type GeneralFormValues = z.infer; export default function GeneralPage() { + const t = useTranslations(); const { client, updateClient } = useClientContext(); const api = createApiClient(useEnvContext()); const [loading, setLoading] = useState(false); @@ -119,18 +121,18 @@ export default function GeneralPage() { updateClient({ name: data.name }); toast({ - title: "Client updated", - description: "The client has been updated." + title: t("clientUpdated"), + description: t("clientUpdatedDescription") }); router.refresh(); } catch (e) { toast({ variant: "destructive", - title: "Failed to update client", + title: t("clientUpdateFailed"), description: formatAxiosError( e, - "An error occurred while updating the client." + t("clientUpdateError") ) }); } finally { @@ -143,10 +145,10 @@ export default function GeneralPage() { - General Settings + {t("generalSettings")} - Configure the general settings for this client + {t("generalSettingsDescription")} @@ -163,15 +165,11 @@ export default function GeneralPage() { name="name" render={({ field }) => ( - Name + {t("name")} - - This is the display name of the - client. - )} /> @@ -181,12 +179,12 @@ export default function GeneralPage() { name="siteIds" render={(field) => ( - Sites + {t("sites")} { @@ -202,9 +200,7 @@ export default function GeneralPage() { sortTags={true} /> - The client will have connectivity to the - selected sites. The sites must be configured - to accept client connections. + {t("sitesDescription")} @@ -222,7 +218,7 @@ export default function GeneralPage() { loading={loading} disabled={loading} > - Save Settings + {t("saveSettings")} diff --git a/src/app/[orgId]/settings/clients/create/page.tsx b/src/app/[orgId]/settings/clients/create/page.tsx index 850504f5..88d2bef2 100644 --- a/src/app/[orgId]/settings/clients/create/page.tsx +++ b/src/app/[orgId]/settings/clients/create/page.tsx @@ -100,7 +100,7 @@ export default function Page() { .refine((val) => val.length > 0, { message: t("siteRequired") }), - subnet: z.string().min(1, { + subnet: z.string().ip().min(1, { message: t("subnetRequired") }) }); @@ -442,14 +442,10 @@ export default function Page() { - - {t("clientNameDescription")} - )} /> diff --git a/src/app/[orgId]/settings/sites/create/page.tsx b/src/app/[orgId]/settings/sites/create/page.tsx index 454f609e..9ea254b1 100644 --- a/src/app/[orgId]/settings/sites/create/page.tsx +++ b/src/app/[orgId]/settings/sites/create/page.tsx @@ -587,11 +587,6 @@ WantedBy=default.target` /> - - {t( - "siteNameDescription" - )} - )} />