You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by "SkyeYoung (via GitHub)" <gi...@apache.org> on 2025/05/06 15:53:54 UTC

[PR] feat: consumer credentials pages [apisix-dashboard]

SkyeYoung opened a new pull request, #3018:
URL: https://github.com/apache/apisix-dashboard/pull/3018

   Please answer these questions before submitting a pull request, **or your PR will get closed**.
   
   **Why submit this pull request?**
   
   - [ ] Bugfix
   - [x] New feature provided
   - [ ] Improve performance
   - [ ] Backport patches
   
   **What changes will this PR take into?**
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] feat: consumer credentials pages [apisix-dashboard]

Posted by "SkyeYoung (via GitHub)" <gi...@apache.org>.
SkyeYoung commented on code in PR #3018:
URL: https://github.com/apache/apisix-dashboard/pull/3018#discussion_r2077057000


##########
src/components/form-slice/FormItemPlugins/PluginCardList.tsx:
##########
@@ -109,12 +118,9 @@ const PluginCardListCore = (props: PluginCardListProps) => {
     },
   }));
 
-  // handle state and useLocalObservable
-  useLayoutEffect(() => {
-    optionsOb.search = search.toLowerCase().trim();
-    optionsOb.plugins = plugins;
-    optionsOb.mode = mode;
-  }, [optionsOb, search, plugins, mode]);
+  useEffect(() => optionsOb.setPlugins(plugins), [optionsOb, plugins]);
+  useEffect(() => optionsOb.setSearch(search), [optionsOb, search]);
+  useEffect(() => optionsOb.setMode(mode), [optionsOb, mode]);

Review Comment:
   follow the suggestions in console.



##########
src/components/form-slice/FormSection/index.tsx:
##########
@@ -66,7 +66,7 @@ export const FormTOCBox = (props: FormTOCBoxProps) => {
   const { children, deps } = props;
   const reinitializeRef = useRef(() => {});
 
-  useDeepCompareEffect(() => {
+  useEffect(() => {
     reinitializeRef.current();
   }, [deps]);

Review Comment:
   follow the suggestions in console.



##########
src/components/form-slice/FormItemPlugins/PluginEditorDrawer.tsx:
##########
@@ -34,9 +34,9 @@ const PluginEditorDrawerCore = (props: PluginEditorDrawerProps) => {
     methods.reset();
   };
 
-  useDeepCompareEffect(() => {
+  useEffect(() => {
     methods.setValue('config', toConfigStr(config));
-  }, [config]);
+  }, [config, methods]);

Review Comment:
   follow the suggestions.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] feat: consumer credentials pages [apisix-dashboard]

Posted by "SkyeYoung (via GitHub)" <gi...@apache.org>.
SkyeYoung commented on code in PR #3018:
URL: https://github.com/apache/apisix-dashboard/pull/3018#discussion_r2077124356


##########
src/routes/consumers/detail.$username/credentials/detail.$id.tsx:
##########
@@ -0,0 +1,127 @@
+import { createFileRoute, useParams } from '@tanstack/react-router';
+import { useTranslation } from 'react-i18next';
+import { useMutation, useSuspenseQuery } from '@tanstack/react-query';
+import PageHeader from '@/components/page/PageHeader';
+import { FormProvider, useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { FormSubmitBtn } from '@/components/form/Btn';
+import { notifications } from '@mantine/notifications';
+import { FormTOCBox } from '@/components/form-slice/FormSection';
+import { Skeleton, Button, Group } from '@mantine/core';
+import { useBoolean } from 'react-use';
+import { useEffect } from 'react';
+import { APISIX } from '@/types/schema/apisix';
+import {
+  getCredentialQueryOptions,
+  putCredentialReq,
+} from '@/apis/credentials';
+import { FormPartCredential } from '@/components/form-slice/FormPartCredential';
+import { pipeProduce } from '@/utils/producer';
+import { DetailCredentialsTabs } from '@/components/page-slice/consumers/DetailCredentialsTabs';
+
+type CredentialFormProps = {
+  readOnly: boolean;
+  setReadOnly: (v: boolean) => void;
+};
+
+const CredentialDetailForm = (props: CredentialFormProps) => {
+  const { readOnly, setReadOnly } = props;
+  const { t } = useTranslation();
+  const { username, id } = useParams({
+    from: '/consumers/detail/$username/credentials/detail/$id',
+  });
+
+  const {
+    data: credentialData,
+    isLoading,
+    refetch,
+  } = useSuspenseQuery(getCredentialQueryOptions(username, id));
+
+  const form = useForm({
+    resolver: zodResolver(APISIX.CredentialPut),
+    shouldUnregister: true,
+    shouldFocusError: true,
+    mode: 'all',
+    disabled: readOnly,
+  });
+
+  useEffect(() => {
+    if (credentialData?.value && !isLoading) {
+      form.reset(credentialData.value);
+    }
+  }, [credentialData, form, isLoading]);
+
+  const putCredential = useMutation({
+    mutationFn: putCredentialReq,
+    async onSuccess() {
+      notifications.show({
+        message: t('consumers.credentials.edit.success'),
+        color: 'green',
+      });
+      await refetch();
+      setReadOnly(true);
+    },
+  });
+
+  if (isLoading) {
+    return <Skeleton height={400} />;
+  }
+
+  return (
+    <FormProvider {...form}>
+      <form
+        onSubmit={form.handleSubmit((d) => {
+          putCredential.mutateAsync({
+            username,
+            ...pipeProduce()(d),
+          });
+        })}
+      >
+        <FormPartCredential showDate />
+        {!readOnly && (
+          <Group>
+            <FormSubmitBtn>{t('form.btn.save')}</FormSubmitBtn>
+            <Button variant="outline" onClick={() => setReadOnly(true)}>
+              {t('form.btn.cancel')}
+            </Button>
+          </Group>
+        )}
+      </form>
+    </FormProvider>
+  );
+};
+
+function RouteComponent() {
+  const { t } = useTranslation();
+  const [readOnly, setReadOnly] = useBoolean(true);
+
+  return (
+    <>
+      <DetailCredentialsTabs />
+      <PageHeader
+        title={t('consumers.credentials.edit.title')}
+        {...(readOnly && {
+          title: t('consumers.credentials.detail.title'),
+          extra: (
+            <Button
+              onClick={() => setReadOnly(false)}
+              size="compact-sm"
+              variant="gradient"
+            >
+              {t('form.btn.edit')}
+            </Button>
+          ),
+        })}
+      />
+      <FormTOCBox>
+        <CredentialDetailForm readOnly={readOnly} setReadOnly={setReadOnly} />
+      </FormTOCBox>
+    </>

Review Comment:
   Users can directly click on the Credentials tab at the top



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] feat: consumer credentials pages [apisix-dashboard]

Posted by "SkyeYoung (via GitHub)" <gi...@apache.org>.
SkyeYoung merged PR #3018:
URL: https://github.com/apache/apisix-dashboard/pull/3018


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] feat: consumer credentials pages [apisix-dashboard]

Posted by "bzp2010 (via GitHub)" <gi...@apache.org>.
bzp2010 commented on code in PR #3018:
URL: https://github.com/apache/apisix-dashboard/pull/3018#discussion_r2077108080


##########
src/routes/consumers/detail.$username/credentials/detail.$id.tsx:
##########
@@ -0,0 +1,127 @@
+import { createFileRoute, useParams } from '@tanstack/react-router';
+import { useTranslation } from 'react-i18next';
+import { useMutation, useSuspenseQuery } from '@tanstack/react-query';
+import PageHeader from '@/components/page/PageHeader';
+import { FormProvider, useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { FormSubmitBtn } from '@/components/form/Btn';
+import { notifications } from '@mantine/notifications';
+import { FormTOCBox } from '@/components/form-slice/FormSection';
+import { Skeleton, Button, Group } from '@mantine/core';
+import { useBoolean } from 'react-use';
+import { useEffect } from 'react';
+import { APISIX } from '@/types/schema/apisix';
+import {
+  getCredentialQueryOptions,
+  putCredentialReq,
+} from '@/apis/credentials';
+import { FormPartCredential } from '@/components/form-slice/FormPartCredential';
+import { pipeProduce } from '@/utils/producer';
+import { DetailCredentialsTabs } from '@/components/page-slice/consumers/DetailCredentialsTabs';
+
+type CredentialFormProps = {
+  readOnly: boolean;
+  setReadOnly: (v: boolean) => void;
+};
+
+const CredentialDetailForm = (props: CredentialFormProps) => {
+  const { readOnly, setReadOnly } = props;
+  const { t } = useTranslation();
+  const { username, id } = useParams({
+    from: '/consumers/detail/$username/credentials/detail/$id',
+  });
+
+  const {
+    data: credentialData,
+    isLoading,
+    refetch,
+  } = useSuspenseQuery(getCredentialQueryOptions(username, id));
+
+  const form = useForm({
+    resolver: zodResolver(APISIX.CredentialPut),
+    shouldUnregister: true,
+    shouldFocusError: true,
+    mode: 'all',
+    disabled: readOnly,
+  });
+
+  useEffect(() => {
+    if (credentialData?.value && !isLoading) {
+      form.reset(credentialData.value);
+    }
+  }, [credentialData, form, isLoading]);
+
+  const putCredential = useMutation({
+    mutationFn: putCredentialReq,
+    async onSuccess() {
+      notifications.show({
+        message: t('consumers.credentials.edit.success'),
+        color: 'green',
+      });
+      await refetch();
+      setReadOnly(true);
+    },
+  });
+
+  if (isLoading) {
+    return <Skeleton height={400} />;
+  }
+
+  return (
+    <FormProvider {...form}>
+      <form
+        onSubmit={form.handleSubmit((d) => {
+          putCredential.mutateAsync({
+            username,
+            ...pipeProduce()(d),
+          });
+        })}
+      >
+        <FormPartCredential showDate />
+        {!readOnly && (
+          <Group>
+            <FormSubmitBtn>{t('form.btn.save')}</FormSubmitBtn>
+            <Button variant="outline" onClick={() => setReadOnly(true)}>
+              {t('form.btn.cancel')}
+            </Button>
+          </Group>
+        )}
+      </form>
+    </FormProvider>
+  );
+};
+
+function RouteComponent() {
+  const { t } = useTranslation();
+  const [readOnly, setReadOnly] = useBoolean(true);
+
+  return (
+    <>
+      <DetailCredentialsTabs />
+      <PageHeader
+        title={t('consumers.credentials.edit.title')}
+        {...(readOnly && {
+          title: t('consumers.credentials.detail.title'),
+          extra: (
+            <Button
+              onClick={() => setReadOnly(false)}
+              size="compact-sm"
+              variant="gradient"
+            >
+              {t('form.btn.edit')}
+            </Button>
+          ),
+        })}
+      />
+      <FormTOCBox>
+        <CredentialDetailForm readOnly={readOnly} setReadOnly={setReadOnly} />
+      </FormTOCBox>
+    </>

Review Comment:
   Is there a button here to return to the credential list?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org