Skip to content

Commit

Permalink
feat(web): implement project alias update in settings page (#1145)
Browse files Browse the repository at this point in the history
* make project alias readonly in accessability page and add copy icon

* wip: add project alias to update project form

* add onProjectAliasCheck

* refactor
  • Loading branch information
nourbalaha authored May 8, 2024
1 parent 38193cc commit bf0b746
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 28 deletions.
15 changes: 11 additions & 4 deletions web/src/components/molecules/Accessibility/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import styled from "@emotion/styled";
import React, { useMemo } from "react";
import React, { useCallback, useMemo } from "react";

import Button from "@reearth-cms/components/atoms/Button";
import Form from "@reearth-cms/components/atoms/Form";
import Icon from "@reearth-cms/components/atoms/Icon";
import InnerContent from "@reearth-cms/components/atoms/InnerContents/basic";
import ContentSection from "@reearth-cms/components/atoms/InnerContents/ContentSection";
import Input from "@reearth-cms/components/atoms/Input";
Expand All @@ -29,7 +30,6 @@ type Props = {
assetState?: boolean;
isSaveDisabled: boolean;
handlePublicUpdate: () => void;
handleAliasChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
handleUpdatedAssetState: (state: boolean) => void;
handleUpdatedModels: (model: Model) => void;
handleSetScope?: (projectScope: PublicScope) => void;
Expand All @@ -43,7 +43,6 @@ const Accessibility: React.FC<Props> = ({
assetState,
isSaveDisabled,
handlePublicUpdate,
handleAliasChange,
handleUpdatedAssetState,
handleUpdatedModels,
handleSetScope,
Expand Down Expand Up @@ -130,6 +129,10 @@ const Accessibility: React.FC<Props> = ({
{ id: 2, name: t("Public"), value: "public" },
];

const handleCopy = useCallback(() => {
if (aliasState) navigator.clipboard.writeText(aliasState);
}, [aliasState]);

return (
<InnerContent title={t("Accessibility")} flexChildren>
<ContentSection title="">
Expand All @@ -149,7 +152,11 @@ const Accessibility: React.FC<Props> = ({
</Select>
</Form.Item>
<Form.Item label={t("Project Alias")}>
<Input value={aliasState} onChange={handleAliasChange} />
<Input
value={aliasState}
suffix={<Icon icon="copy" onClick={handleCopy} />}
disabled
/>
</Form.Item>
</ItemsWrapper>
<TableWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ export type Props = {
onProjectAliasCheck: (alias: string) => Promise<boolean>;
};

const MINIMUM_LENGTH = 4;

const initialValues: FormValues = {
name: "",
alias: "",
Expand Down Expand Up @@ -93,9 +91,8 @@ const ProjectCreationModal: React.FC<Props> = ({
{
message: t("Project alias is not valid"),
required: true,
min: MINIMUM_LENGTH,
validator: async (_, value) => {
if (!validateKey(value) || value.length <= MINIMUM_LENGTH) {
if (!validateKey(value) || value.length <= 4) {
return Promise.reject();
}
const isProjectAliasAvailable = await onProjectAliasCheck(value);
Expand Down
65 changes: 55 additions & 10 deletions web/src/components/molecules/ProjectSettings/GeneralForm.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,82 @@
import styled from "@emotion/styled";
import { useCallback } from "react";
import { useCallback, useState } from "react";

import Button from "@reearth-cms/components/atoms/Button";
import Form from "@reearth-cms/components/atoms/Form";
import Form, { FieldError } from "@reearth-cms/components/atoms/Form";
import Input from "@reearth-cms/components/atoms/Input";
import TextArea from "@reearth-cms/components/atoms/TextArea";
import { useT } from "@reearth-cms/i18n";
import { validateKey } from "@reearth-cms/utils/regex";

import { Project } from "../Workspace/types";

export type Props = {
project?: Project;
onProjectUpdate: (name?: string | undefined, description?: string | undefined) => Promise<void>;
onProjectUpdate: (name?: string, alias?: string, description?: string) => Promise<void>;
onProjectAliasCheck: (alias: string) => Promise<boolean>;
};

const ProjectGeneralForm: React.FC<Props> = ({ project, onProjectUpdate }) => {
const ProjectGeneralForm: React.FC<Props> = ({ project, onProjectUpdate, onProjectAliasCheck }) => {
const [form] = Form.useForm();
const t = useT();
const [buttonDisabled, setButtonDisabled] = useState(false);

const handleSubmit = useCallback(() => {
form
.validateFields()
.then(async values => {
onProjectUpdate(values.name, values.description);
await onProjectUpdate(values.name, values.alias, values.description);
})
.catch(info => {
console.log("Validate Failed:", info);
.catch(fieldsError => {
console.log("Validate Failed:", fieldsError);
});
}, [form, onProjectUpdate]);

const handleFormValues = useCallback(() => {
form
.validateFields()
.then(() => {
setButtonDisabled(false);
})
.catch(fieldsError => {
setButtonDisabled(
fieldsError.errorFields.some((item: FieldError) => item.errors.length > 0),
);
});
}, [form]);

return (
<StyledForm form={form} layout="vertical" autoComplete="off" initialValues={project}>
<Form.Item name="name" label={t("Name")}>
<StyledForm
form={form}
layout="vertical"
autoComplete="off"
initialValues={project}
onFinish={handleSubmit}
onValuesChange={handleFormValues}>
<Form.Item
name="name"
label={t("Name")}
rules={[{ required: true, message: t("Please input the name of project!") }]}>
<Input />
</Form.Item>
<Form.Item
name="alias"
label={t("Alias")}
rules={[
{
required: true,
message: t("Project alias is not valid"),
validator: async (_, value) => {
if (!validateKey(value) || value.length <= 4) {
return Promise.reject();
}
const isProjectAliasAvailable = await onProjectAliasCheck(value);
return isProjectAliasAvailable || project?.alias === value
? Promise.resolve()
: Promise.reject();
},
},
]}>
<Input />
</Form.Item>
<Form.Item
Expand All @@ -41,7 +86,7 @@ const ProjectGeneralForm: React.FC<Props> = ({ project, onProjectUpdate }) => {
<TextArea rows={4} />
</Form.Item>
<Form.Item>
<Button onClick={handleSubmit} type="primary" htmlType="submit">
<Button type="primary" htmlType="submit" disabled={buttonDisabled}>
{t("Save changes")}
</Button>
</Form.Item>
Expand Down
10 changes: 8 additions & 2 deletions web/src/components/molecules/ProjectSettings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,29 @@ import { Project, Role } from "../Workspace/types";

export type Props = {
project?: Project;
onProjectUpdate: (name?: string | undefined, description?: string | undefined) => Promise<void>;
onProjectUpdate: (name?: string, alias?: string, description?: string) => Promise<void>;
onProjectRequestRolesUpdate: (role?: Role[] | null) => Promise<void>;
onProjectDelete: () => Promise<void>;
onProjectAliasCheck: (alias: string) => Promise<boolean>;
};

const ProjectSettings: React.FC<Props> = ({
project,
onProjectDelete,
onProjectUpdate,
onProjectRequestRolesUpdate,
onProjectAliasCheck,
}) => {
const t = useT();

return (
<InnerContent title={`${t("Project Settings")} / ${project?.name}`}>
<ContentSection title={t("General")}>
<ProjectGeneralForm project={project} onProjectUpdate={onProjectUpdate} />
<ProjectGeneralForm
project={project}
onProjectUpdate={onProjectUpdate}
onProjectAliasCheck={onProjectAliasCheck}
/>
</ContentSection>
<ContentSection title={t("Request")}>
<ProjectRequestOptions
Expand Down
5 changes: 0 additions & 5 deletions web/src/components/organisms/Project/Accessibility/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,6 @@ export default () => {
t,
]);

const handleAliasChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setAlias(e.currentTarget.value);
}, []);

const handleUpdatedAssetState = useCallback((state: boolean) => {
setAssetState(state);
}, []);
Expand Down Expand Up @@ -155,7 +151,6 @@ export default () => {
assetState,
isSaveDisabled,
handlePublicUpdate,
handleAliasChange,
handleUpdatedAssetState,
handleUpdatedModels,
handleSetScope,
Expand Down
2 changes: 0 additions & 2 deletions web/src/components/organisms/Project/Accessibility/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const Accessibility: React.FC = () => {
assetState,
isSaveDisabled,
handlePublicUpdate,
handleAliasChange,
handleUpdatedAssetState,
handleUpdatedModels,
handleSetScope,
Expand All @@ -26,7 +25,6 @@ const Accessibility: React.FC = () => {
assetState={assetState}
isSaveDisabled={isSaveDisabled}
handlePublicUpdate={handlePublicUpdate}
handleAliasChange={handleAliasChange}
handleUpdatedAssetState={handleUpdatedAssetState}
handleUpdatedModels={handleUpdatedModels}
handleSetScope={handleSetScope}
Expand Down
19 changes: 18 additions & 1 deletion web/src/components/organisms/Project/settings/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
useUpdateProjectMutation,
useDeleteProjectMutation,
Role as GQLRole,
useCheckProjectAliasLazyQuery,
} from "@reearth-cms/gql/graphql-client-api";
import { useT } from "@reearth-cms/i18n";
import { useWorkspace } from "@reearth-cms/state";
Expand Down Expand Up @@ -54,12 +55,13 @@ export default ({ projectId }: Params) => {
});

const handleProjectUpdate = useCallback(
async (name?: string, description?: string) => {
async (name?: string, alias?: string, description?: string) => {
if (!projectId || !name) return;
const result = await updateProjectMutation({
variables: {
projectId,
name,
alias,
description,
requestRoles: project?.requestRoles as GQLRole[],
},
Expand Down Expand Up @@ -115,6 +117,20 @@ export default ({ projectId }: Params) => {
[assetModalOpened, setOpenAssets],
);

const [CheckProjectAlias] = useCheckProjectAliasLazyQuery({
fetchPolicy: "no-cache",
});

const handleProjectAliasCheck = useCallback(
async (alias: string) => {
if (!alias) return false;

const response = await CheckProjectAlias({ variables: { alias } });
return response.data ? response.data.checkProjectAlias.available : false;
},
[CheckProjectAlias],
);

return {
project,
loading,
Expand All @@ -123,6 +139,7 @@ export default ({ projectId }: Params) => {
handleProjectUpdate,
handleProjectRequestRolesUpdate,
handleProjectDelete,
handleProjectAliasCheck,
assetModalOpened,
toggleAssetModal,
};
Expand Down
2 changes: 2 additions & 0 deletions web/src/components/organisms/Project/settings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const ProjectSettings: React.FC = () => {
handleProjectDelete,
handleProjectUpdate,
handleProjectRequestRolesUpdate,
handleProjectAliasCheck,
} = useHooks({
projectId,
});
Expand All @@ -26,6 +27,7 @@ const ProjectSettings: React.FC = () => {
onProjectDelete={handleProjectDelete}
onProjectUpdate={handleProjectUpdate}
onProjectRequestRolesUpdate={handleProjectRequestRolesUpdate}
onProjectAliasCheck={handleProjectAliasCheck}
/>
);
};
Expand Down
1 change: 1 addition & 0 deletions web/src/i18n/translations/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ Edit: ''
Are you sure you want to delete this project?: ''
Delete Project: ''
Permanently removes your project and all of its contents from Re:Earth CMS. This action is not reversible, so please continue with caution.: ''
Alias: ''
Write something here to describe this record.: ''
Project Settings: ''
Need request: ''
Expand Down
1 change: 1 addition & 0 deletions web/src/i18n/translations/ja.yml
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ Edit: 編集
Are you sure you want to delete this project?: 本当にプロジェクトを削除してもよろしいですか?
Delete Project: プロジェクトを削除
Permanently removes your project and all of its contents from Re:Earth CMS. This action is not reversible, so please continue with caution.: プロジェクトとその内容をRe:Earth CMSから完全に削除します。このアクションは元に戻せません。
Alias: エイリアス
Write something here to describe this record.: このレコードの説明を入力してください。
Project Settings: プロジェクト設定
Need request: リクエストの要否
Expand Down

0 comments on commit bf0b746

Please sign in to comment.