From ce0bc477c20fdbcdc141e8a20485dec0ca9c3294 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Thu, 4 Nov 2021 14:32:42 -0600 Subject: [PATCH] ui(admin): fix egg variables --- .../Variables/VariableCreationService.php | 4 +- resources/scripts/api/admin/egg.ts | 26 +++++++++- .../api/admin/eggs/createEggVariable.ts | 37 ++++++++------- .../api/admin/eggs/updateEggVariables.ts | 37 +++++++-------- resources/scripts/api/admin/transformers.ts | 2 +- .../admin/nests/eggs/EggInstallContainer.tsx | 10 +++- .../components/admin/nests/eggs/EggRouter.tsx | 35 ++++++++------ .../admin/nests/eggs/EggSettingsContainer.tsx | 22 +++++++-- .../nests/eggs/EggVariablesContainer.tsx | 47 ++++++++++--------- .../admin/nests/eggs/NewVariableButton.tsx | 34 ++++++++------ .../admin/servers/NewServerContainer.tsx | 1 - .../components/admin/servers/ServerRouter.tsx | 2 +- 12 files changed, 157 insertions(+), 100 deletions(-) diff --git a/app/Services/Eggs/Variables/VariableCreationService.php b/app/Services/Eggs/Variables/VariableCreationService.php index 4f866c8c9..79010d4f5 100644 --- a/app/Services/Eggs/Variables/VariableCreationService.php +++ b/app/Services/Eggs/Variables/VariableCreationService.php @@ -55,8 +55,8 @@ class VariableCreationService 'description' => $data['description'] ?? '', 'env_variable' => $data['env_variable'] ?? '', 'default_value' => $data['default_value'] ?? '', - 'user_viewable' => in_array('user_viewable', $options), - 'user_editable' => in_array('user_editable', $options), + 'user_viewable' => $data['user_viewable'], + 'user_editable' => $data['user_editable'], 'rules' => $data['rules'] ?? '', ]); diff --git a/resources/scripts/api/admin/egg.ts b/resources/scripts/api/admin/egg.ts index ec6c03e1d..4fd5e7844 100644 --- a/resources/scripts/api/admin/egg.ts +++ b/resources/scripts/api/admin/egg.ts @@ -2,6 +2,9 @@ import { Model, UUID, WithRelationships, withRelationships } from '@/api/admin/i import { Nest } from '@/api/admin/nest'; import http, { QueryBuilderParams, withQueryBuilderParams } from '@/api/http'; import { AdminTransformers } from '@/api/admin/transformers'; +import { AxiosError } from 'axios'; +import { useRouteMatch } from 'react-router-dom'; +import useSWR, { SWRResponse } from 'swr'; export interface Egg extends Model { id: number; @@ -39,16 +42,22 @@ export interface EggVariable extends Model { defaultValue: string; isUserViewable: boolean; isUserEditable: boolean; - isRequired: boolean; + // isRequired: boolean; rules: string; createdAt: Date; updatedAt: Date; } +/** + * A standard API response with the minimum viable details for the frontend + * to correctly render a egg. + */ +type LoadedEgg = WithRelationships; + /** * Gets a single egg from the database and returns it. */ -export const getEgg = async (id: number | string): Promise> => { +export const getEgg = async (id: number | string): Promise => { const { data } = await http.get(`/api/application/eggs/${id}`, { params: { include: [ 'nest', 'variables' ], @@ -73,3 +82,16 @@ export const exportEgg = async (eggId: number): Promise> => const { data } = await http.get(`/api/application/eggs/${eggId}/export`); return data; }; + +/** + * Returns an SWR instance by automatically loading in the server for the currently + * loaded route match in the admin area. + */ +export const useEggFromRoute = (): SWRResponse => { + const { params } = useRouteMatch<{ id: string }>(); + + return useSWR(`/api/application/eggs/${params.id}`, async () => getEgg(params.id), { + revalidateOnMount: false, + revalidateOnFocus: false, + }); +}; diff --git a/resources/scripts/api/admin/eggs/createEggVariable.ts b/resources/scripts/api/admin/eggs/createEggVariable.ts index dd5ee38fe..72f2c314d 100644 --- a/resources/scripts/api/admin/eggs/createEggVariable.ts +++ b/resources/scripts/api/admin/eggs/createEggVariable.ts @@ -1,21 +1,22 @@ import http from '@/api/http'; -import { EggVariable, rawDataToEggVariable } from '@/api/admin/eggs/getEgg'; +import { EggVariable } from '@/api/admin/egg'; +import { AdminTransformers } from '@/api/admin/transformers'; -export default (eggId: number, variable: Omit): Promise => { - return new Promise((resolve, reject) => { - http.post( - `/api/application/eggs/${eggId}/variables`, - { - name: variable.name, - description: variable.description, - env_variable: variable.envVariable, - default_value: variable.defaultValue, - user_viewable: variable.userViewable, - user_editable: variable.userEditable, - rules: variable.rules, - }, - ) - .then(({ data }) => resolve(rawDataToEggVariable(data))) - .catch(reject); - }); +export type CreateEggVariable = Omit; + +export default async (eggId: number, variable: CreateEggVariable): Promise => { + const { data } = await http.post( + `/api/application/eggs/${eggId}/variables`, + { + name: variable.name, + description: variable.description, + env_variable: variable.environmentVariable, + default_value: variable.defaultValue, + user_viewable: variable.isUserViewable, + user_editable: variable.isUserEditable, + rules: variable.rules, + }, + ); + + return AdminTransformers.toEggVariable(data); }; diff --git a/resources/scripts/api/admin/eggs/updateEggVariables.ts b/resources/scripts/api/admin/eggs/updateEggVariables.ts index 3ccfe3d38..b78273341 100644 --- a/resources/scripts/api/admin/eggs/updateEggVariables.ts +++ b/resources/scripts/api/admin/eggs/updateEggVariables.ts @@ -1,22 +1,21 @@ import http from '@/api/http'; -import { EggVariable, rawDataToEggVariable } from '@/api/admin/eggs/getEgg'; +import { EggVariable } from '@/api/admin/egg'; +import { AdminTransformers } from '@/api/admin/transformers'; -export default (eggId: number, variables: Omit[]): Promise => { - return new Promise((resolve, reject) => { - http.patch( - `/api/application/eggs/${eggId}/variables`, - variables.map(variable => ({ - id: variable.id, - name: variable.name, - description: variable.description, - env_variable: variable.envVariable, - default_value: variable.defaultValue, - user_viewable: variable.userViewable, - user_editable: variable.userEditable, - rules: variable.rules, - })), - ) - .then(({ data }) => resolve((data.data || []).map(rawDataToEggVariable))) - .catch(reject); - }); +export default async (eggId: number, variables: Omit[]): Promise => { + const { data } = await http.patch( + `/api/application/eggs/${eggId}/variables`, + variables.map(variable => ({ + id: variable.id, + name: variable.name, + description: variable.description, + env_variable: variable.environmentVariable, + default_value: variable.defaultValue, + user_viewable: variable.isUserViewable, + user_editable: variable.isUserEditable, + rules: variable.rules, + })), + ); + + return data.data.map(AdminTransformers.toEggVariable); }; diff --git a/resources/scripts/api/admin/transformers.ts b/resources/scripts/api/admin/transformers.ts index b306a41c8..bed54fb8f 100644 --- a/resources/scripts/api/admin/transformers.ts +++ b/resources/scripts/api/admin/transformers.ts @@ -168,7 +168,7 @@ export class AdminTransformers { defaultValue: attributes.default_value, isUserViewable: attributes.user_viewable, isUserEditable: attributes.user_editable, - isRequired: attributes.required, + // isRequired: attributes.required, rules: attributes.rules, createdAt: new Date(attributes.created_at), updatedAt: new Date(attributes.updated_at), diff --git a/resources/scripts/components/admin/nests/eggs/EggInstallContainer.tsx b/resources/scripts/components/admin/nests/eggs/EggInstallContainer.tsx index 1ea32071b..5864a7567 100644 --- a/resources/scripts/components/admin/nests/eggs/EggInstallContainer.tsx +++ b/resources/scripts/components/admin/nests/eggs/EggInstallContainer.tsx @@ -1,4 +1,4 @@ -import { Egg } from '@/api/admin/eggs/getEgg'; +import { useEggFromRoute } from '@/api/admin/egg'; import updateEgg from '@/api/admin/eggs/updateEgg'; import Field from '@/components/elements/Field'; import useFlash from '@/plugins/useFlash'; @@ -18,9 +18,15 @@ interface Values { scriptInstall: string; } -export default function EggInstallContainer ({ egg }: { egg: Egg }) { +export default function EggInstallContainer () { const { clearFlashes, clearAndAddHttpError } = useFlash(); + const { data: egg } = useEggFromRoute(); + + if (!egg) { + return null; + } + let fetchFileContent: (() => Promise) | null = null; const submit = async (values: Values, { setSubmitting }: FormikHelpers) => { diff --git a/resources/scripts/components/admin/nests/eggs/EggRouter.tsx b/resources/scripts/components/admin/nests/eggs/EggRouter.tsx index 0e11921fa..14d4cf544 100644 --- a/resources/scripts/components/admin/nests/eggs/EggRouter.tsx +++ b/resources/scripts/components/admin/nests/eggs/EggRouter.tsx @@ -1,10 +1,11 @@ +import { useEggFromRoute } from '@/api/admin/egg'; import EggInstallContainer from '@/components/admin/nests/eggs/EggInstallContainer'; import EggVariablesContainer from '@/components/admin/nests/eggs/EggVariablesContainer'; -import React from 'react'; +import useFlash from '@/plugins/useFlash'; +import React, { useEffect } from 'react'; import { useLocation } from 'react-router'; import tw from 'twin.macro'; import { Route, Switch, useRouteMatch } from 'react-router-dom'; -import getEgg from '@/api/admin/eggs/getEgg'; import AdminContentBlock from '@/components/admin/AdminContentBlock'; import Spinner from '@/components/elements/Spinner'; import FlashMessageRender from '@/components/FlashMessageRender'; @@ -13,18 +14,24 @@ import EggSettingsContainer from '@/components/admin/nests/eggs/EggSettingsConta const EggRouter = () => { const location = useLocation(); - const match = useRouteMatch<{ id?: string }>(); + const match = useRouteMatch(); - const { data: egg } = getEgg(Number(match.params?.id)); + const { clearFlashes, clearAndAddHttpError } = useFlash(); + const { data: egg, error, isValidating, mutate } = useEggFromRoute(); - if (egg === undefined) { + useEffect(() => { + mutate(); + }, []); + + useEffect(() => { + if (!error) clearFlashes('egg'); + if (error) clearAndAddHttpError({ error, key: 'egg' }); + }, [ error ]); + + if (!egg || (error && isValidating)) { return ( - - - -
- -
+ + ); } @@ -62,15 +69,15 @@ const EggRouter = () => { - + - + - +
diff --git a/resources/scripts/components/admin/nests/eggs/EggSettingsContainer.tsx b/resources/scripts/components/admin/nests/eggs/EggSettingsContainer.tsx index e411de441..70faac949 100644 --- a/resources/scripts/components/admin/nests/eggs/EggSettingsContainer.tsx +++ b/resources/scripts/components/admin/nests/eggs/EggSettingsContainer.tsx @@ -1,3 +1,4 @@ +import { useEggFromRoute } from '@/api/admin/egg'; import updateEgg from '@/api/admin/eggs/updateEgg'; import EggDeleteButton from '@/components/admin/nests/eggs/EggDeleteButton'; import EggExportButton from '@/components/admin/nests/eggs/EggExportButton'; @@ -13,7 +14,6 @@ import { faDocker } from '@fortawesome/free-brands-svg-icons'; import { faEgg, faFireAlt, faMicrochip, faTerminal } from '@fortawesome/free-solid-svg-icons'; import React, { forwardRef, useImperativeHandle, useRef } from 'react'; import AdminBox from '@/components/admin/AdminBox'; -import { Egg } from '@/api/admin/eggs/getEgg'; import { useHistory } from 'react-router-dom'; import tw from 'twin.macro'; import { object } from 'yup'; @@ -45,7 +45,13 @@ export function EggInformationContainer () { ); } -function EggDetailsContainer ({ egg }: { egg: Egg }) { +function EggDetailsContainer () { + const { data: egg } = useEggFromRoute(); + + if (!egg) { + return null; + } + return (
@@ -200,12 +206,18 @@ interface Values { configFiles: string; } -export default function EggSettingsContainer ({ egg }: { egg: Egg }) { +export default function EggSettingsContainer () { const history = useHistory(); + const ref = useRef(); + const { clearFlashes, clearAndAddHttpError } = useFlash(); - const ref = useRef(); + const { data: egg } = useEggFromRoute(); + + if (!egg) { + return null; + } const submit = async (values: Values, { setSubmitting }: FormikHelpers) => { clearFlashes('egg'); @@ -240,7 +252,7 @@ export default function EggSettingsContainer ({ egg }: { egg: Egg }) {
- +
diff --git a/resources/scripts/components/admin/nests/eggs/EggVariablesContainer.tsx b/resources/scripts/components/admin/nests/eggs/EggVariablesContainer.tsx index 7fb67d7d9..0ddef6616 100644 --- a/resources/scripts/components/admin/nests/eggs/EggVariablesContainer.tsx +++ b/resources/scripts/components/admin/nests/eggs/EggVariablesContainer.tsx @@ -5,7 +5,7 @@ import { Form, Formik, FormikHelpers, useFormikContext } from 'formik'; import React, { useState } from 'react'; import tw from 'twin.macro'; import { array, boolean, object, string } from 'yup'; -import getEgg, { Egg, EggVariable } from '@/api/admin/eggs/getEgg'; +import { EggVariable, useEggFromRoute } from '@/api/admin/egg'; import updateEggVariables from '@/api/admin/eggs/updateEggVariables'; import NewVariableButton from '@/components/admin/nests/eggs/NewVariableButton'; import AdminBox from '@/components/admin/AdminBox'; @@ -19,10 +19,10 @@ import { TrashIcon } from '@heroicons/react/outline'; export const validationSchema = object().shape({ name: string().required().min(1).max(191), description: string(), - envVariable: string().required().min(1).max(191), + environmentVariable: string().required().min(1).max(191), defaultValue: string(), - userViewable: boolean().required(), - userEditable: boolean().required(), + isUserViewable: boolean().required(), + isUserEditable: boolean().required(), rules: string().required(), }); @@ -47,8 +47,8 @@ export function EggVariableForm ({ prefix }: { prefix: string }) { @@ -63,14 +63,14 @@ export function EggVariableForm ({ prefix }: { prefix: string }) {
@@ -95,7 +95,7 @@ function EggVariableDeleteButton ({ onClick }: { onClick: (success: () => void) setLoading(true); onClick(() => { - setLoading(false); + //setLoading(false); }); }; @@ -140,14 +140,18 @@ function EggVariableBox ({ onDeleteClick, variable, prefix }: { onDeleteClick: ( ); } -export default function EggVariablesContainer ({ egg }: { egg: Egg }) { +export default function EggVariablesContainer () { const { clearAndAddHttpError } = useFlash(); - const { mutate } = getEgg(egg.id); + const { data: egg, mutate } = useEggFromRoute(); + + if (!egg) { + return null; + } const submit = (values: EggVariable[], { setSubmitting }: FormikHelpers) => { updateEggVariables(egg.id, values) - .then(async (variables) => await mutate(egg => ({ ...egg!, relations: { variables: variables } }))) + .then(async () => await mutate()) .catch(error => clearAndAddHttpError({ key: 'egg', error })) .then(() => setSubmitting(false)); }; @@ -155,17 +159,17 @@ export default function EggVariablesContainer ({ egg }: { egg: Egg }) { return ( {({ isSubmitting, isValid }) => (
- {egg.relations?.variables?.length === 0 ? + {egg.relationships.variables?.length === 0 ? : -
- {egg.relations?.variables?.map((v, i) => ( +
+ {egg.relationships.variables.map((v, i) => ( { await mutate(egg => ({ ...egg!, - relations: { - variables: egg!.relations.variables!.filter(v2 => v.id === v2.id), + relationships: { + ...egg!.relationships, + variables: egg!.relationships.variables!.filter(v2 => v.id === v2.id), }, })); success(); @@ -190,7 +195,7 @@ export default function EggVariablesContainer ({ egg }: { egg: Egg }) {
- + -
diff --git a/resources/scripts/components/admin/servers/NewServerContainer.tsx b/resources/scripts/components/admin/servers/NewServerContainer.tsx index d5b37df5b..e92f46cd6 100644 --- a/resources/scripts/components/admin/servers/NewServerContainer.tsx +++ b/resources/scripts/components/admin/servers/NewServerContainer.tsx @@ -146,7 +146,6 @@ export default () => { const { clearFlashes, clearAndAddHttpError } = useFlash(); const submit = (r: CreateServerRequest, { setSubmitting }: FormikHelpers) => { - console.log(r); clearFlashes('server:create'); createServer(r) diff --git a/resources/scripts/components/admin/servers/ServerRouter.tsx b/resources/scripts/components/admin/servers/ServerRouter.tsx index 9988b28c0..1d0d14ce3 100644 --- a/resources/scripts/components/admin/servers/ServerRouter.tsx +++ b/resources/scripts/components/admin/servers/ServerRouter.tsx @@ -32,7 +32,7 @@ export default () => { if (!server || (error && isValidating)) { return ( - ; + ); }