Skip to content

Commit

Permalink
Merge pull request #148 from kitcc-org/71-users-add-page
Browse files Browse the repository at this point in the history
ユーザー作成ページの実装
  • Loading branch information
Kosei805 authored Nov 19, 2024
2 parents 4842573 + 37d0c2d commit bddebba
Show file tree
Hide file tree
Showing 21 changed files with 560 additions and 68 deletions.
4 changes: 2 additions & 2 deletions frontend/app/components/header/HeaderUsersMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ const HeaderUsersMenu = () => {
</Menu.Item>
<Menu.Item
leftSection={<FaUserPlus />}
onClick={() => navigate('/home/user-add')}
onClick={() => navigate('/home/users/create')}
>
ユーザー追加
ユーザー作成
</Menu.Item>
</Menu.Dropdown>
</Menu>
Expand Down
4 changes: 1 addition & 3 deletions frontend/app/components/login/LoginFormComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ import LoginPasswordForm from './LoginPasswordForm';
import LoginSubmitButton from './LoginSubmitButton';

interface LoginFormComponentProps {
isPending: boolean;
form: UseFormReturnType<LoginBody, (values: LoginBody) => LoginBody>;
handleSubmit: (props: LoginBody) => void;
}

const LoginFormComponent = ({
isPending,
form,
handleSubmit,
}: LoginFormComponentProps) => {
Expand All @@ -25,7 +23,7 @@ const LoginFormComponent = ({
<LoginFormTitle />
<LoginEmailForm form={form} />
<LoginPasswordForm form={form} />
<LoginSubmitButton isPending={isPending} />
<LoginSubmitButton />
<LoginFormHelpText />
</FormLayout>
</FormBaseLayout>
Expand Down
12 changes: 2 additions & 10 deletions frontend/app/components/login/LoginSubmitButton.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import { Button } from '@mantine/core';

interface LoginSubmitButtonProps {
isPending: boolean;
}

const LoginSubmitButton = ({ isPending }: LoginSubmitButtonProps) => {
return (
<Button type="submit" disabled={isPending}>
ログイン
</Button>
);
const LoginSubmitButton = () => {
return <Button type="submit">ログイン</Button>;
};

export default LoginSubmitButton;
43 changes: 43 additions & 0 deletions frontend/app/components/user-create/PasswordCopyButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ActionIcon, CopyButton, Tooltip } from '@mantine/core';
import { UseFormReturnType } from '@mantine/form';
import { CreateUserBody } from 'client/client.schemas';
import { IoCheckmark, IoCopyOutline } from 'react-icons/io5';
import { successNotification } from '~/utils/notification';

interface PasswordCopyButtonProps {
form: UseFormReturnType<
CreateUserBody,
(values: CreateUserBody) => CreateUserBody
>;
generated: boolean; // パスワードがすでに生成されているかどうか
}

const PasswordCopyButton = ({ form, generated }: PasswordCopyButtonProps) => {
return (
<CopyButton value={form.getValues().password} timeout={2000}>
{({ copied, copy }) => (
<Tooltip
label={
copied ? 'パスワードをコピーしました' : 'パスワードをコピーする'
}
withArrow
position="right"
>
<ActionIcon
color={copied ? 'teal' : 'black'}
variant="subtle"
disabled={!generated}
onClick={() => {
copy();
successNotification('パスワードをコピーしました');
}}
>
{copied ? <IoCheckmark /> : <IoCopyOutline />}
</ActionIcon>
</Tooltip>
)}
</CopyButton>
);
};

export default PasswordCopyButton;
21 changes: 21 additions & 0 deletions frontend/app/components/user-create/PasswordGenerateButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Button } from '@mantine/core';
import { FaKey } from 'react-icons/fa';

interface PasswordGenerateButtonProps {
handlePasswordGenButtonClick: () => void;
}

const PasswordGenerateButton = ({
handlePasswordGenButtonClick,
}: PasswordGenerateButtonProps) => {
return (
<Button
onClick={() => handlePasswordGenButtonClick()}
leftSection={<FaKey />}
>
パスワードを生成する
</Button>
);
};

export default PasswordGenerateButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Button } from '@mantine/core';
import PasswordValidProgress from './PasswordValidProgress';

interface PasswordValidCountComponentProps {
counts: number;
}

const PasswordValidCountComponent = ({
counts,
}: PasswordValidCountComponentProps) => {
const fmtedCounts = counts.toString().padStart(2, ' ');
return (
<Button
disabled
leftSection={<PasswordValidProgress counts={counts} />}
>{`パスワードの有効時間:${fmtedCounts}秒`}</Button>
);
};

export default PasswordValidCountComponent;
18 changes: 18 additions & 0 deletions frontend/app/components/user-create/PasswordValidProgress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { RingProgress } from '@mantine/core';

interface PasswordValidProgressProps {
counts: number;
}

const PasswordValidProgress = ({ counts }: PasswordValidProgressProps) => {
return (
<RingProgress
size={30}
thickness={5}
transitionDuration={1000}
sections={[{ value: Math.floor((counts / 30) * 100), color: 'gray' }]}
/>
);
};

export default PasswordValidProgress;
48 changes: 48 additions & 0 deletions frontend/app/components/user-create/UserCreateComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import FormLayout from '../layouts/FormLayout';
import { CreateUserBody } from 'client/client.schemas';
import { UseFormReturnType } from '@mantine/form';
import UserCreateTitle from './UserCreateTitle';
import UserCreateEmailForm from './UserCreateEmailForm';
import UserCreateNameForm from './UserCreateNameForm';
import { Container } from '@mantine/core';
import UserCreatePasswordForm from './UserCreatePasswordForm';
import UserCreateSubmitButton from './UserCreateSubmitButton';
import UserCreatePasswordComponent from './UserCreatePasswordComponent';

interface UserCreateComponentProps {
form: UseFormReturnType<
CreateUserBody,
(values: CreateUserBody) => CreateUserBody
>;
handleSubmit: (props: CreateUserBody) => void;
handlePasswordGenButtonClick: () => void;
copied: boolean;
counts: number;
}

const UserCreateComponent = ({
form,
handleSubmit,
handlePasswordGenButtonClick,
copied,
counts,
}: UserCreateComponentProps) => {
return (
<Container size="sm">
<FormLayout<CreateUserBody> form={form} handleSubmit={handleSubmit}>
<UserCreateTitle />
<UserCreateNameForm form={form} />
<UserCreateEmailForm form={form} />
<UserCreatePasswordForm form={form} copied={copied} />
<UserCreatePasswordComponent
handlePasswordGenButtonClick={handlePasswordGenButtonClick}
copied={copied}
counts={counts}
/>
<UserCreateSubmitButton copied={copied} />
</FormLayout>
</Container>
);
};

export default UserCreateComponent;
25 changes: 25 additions & 0 deletions frontend/app/components/user-create/UserCreateEmailForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { TextInput } from '@mantine/core';
import type { UseFormReturnType } from '@mantine/form';
import type { CreateUserBody } from 'client/client.schemas';

interface UserCreateEmailFormProps {
form: UseFormReturnType<
CreateUserBody,
(values: CreateUserBody) => CreateUserBody
>;
}

const UserCreateEmailForm = ({ form }: UserCreateEmailFormProps) => {
return (
<TextInput
label="メールアドレス"
withAsterisk
autoComplete="email"
key={form.key('email')}
aria-label="メールアドレス"
{...form.getInputProps('email')}
/>
);
};

export default UserCreateEmailForm;
25 changes: 25 additions & 0 deletions frontend/app/components/user-create/UserCreateNameForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { TextInput } from '@mantine/core';
import { UseFormReturnType } from '@mantine/form';
import { CreateUserBody } from 'client/client.schemas';

interface UserCreateNameFormProps {
form: UseFormReturnType<
CreateUserBody,
(values: CreateUserBody) => CreateUserBody
>;
}

const UserCreateNameForm = ({ form }: UserCreateNameFormProps) => {
return (
<TextInput
label="ユーザー名"
withAsterisk
autoComplete="username"
key={form.key('name')}
aria-label="ユーザー名"
{...form.getInputProps('name')}
/>
);
};

export default UserCreateNameForm;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import PasswordGenerateButton from './PasswordGenerateButton';
import PasswordValidCountComponent from './PasswordValidCountComponent';

interface UserCreatePasswordComponentProps {
handlePasswordGenButtonClick: () => void;
copied: boolean;
counts: number;
}

const UserCreatePasswordComponent = ({
handlePasswordGenButtonClick,
copied,
counts,
}: UserCreatePasswordComponentProps) => {
if (copied) {
return <PasswordValidCountComponent counts={counts} />;
} else {
return (
<PasswordGenerateButton
handlePasswordGenButtonClick={handlePasswordGenButtonClick}
/>
);
}
};

export default UserCreatePasswordComponent;
32 changes: 32 additions & 0 deletions frontend/app/components/user-create/UserCreatePasswordForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { TextInput } from '@mantine/core';
import type { UseFormReturnType } from '@mantine/form';
import type { CreateUserBody } from 'client/client.schemas';
import PasswordCopyButton from './PasswordCopyButton';

interface UserCreatePasswordFormProps {
form: UseFormReturnType<
CreateUserBody,
(values: CreateUserBody) => CreateUserBody
>;
copied: boolean;
}

const UserCreatePasswordForm = ({
form,
copied,
}: UserCreatePasswordFormProps) => {
return (
<TextInput
disabled
label="パスワード"
withAsterisk
autoComplete="current-password"
key={form.key('password')}
aria-label="パスワード"
rightSection={<PasswordCopyButton form={form} generated={copied} />}
{...form.getInputProps('password')}
/>
);
};

export default UserCreatePasswordForm;
21 changes: 21 additions & 0 deletions frontend/app/components/user-create/UserCreateSubmitButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Button } from '@mantine/core';
import { RiUserAddFill } from 'react-icons/ri';

interface UserCreateSubmitButtonProps {
copied: boolean;
}

const UserCreateSubmitButton = ({ copied }: UserCreateSubmitButtonProps) => {
return (
<Button
type="submit"
color="yellow"
leftSection={<RiUserAddFill />}
disabled={!copied}
>
{copied ? '作成' : 'パスワードを生成してください'}
</Button>
);
};

export default UserCreateSubmitButton;
15 changes: 15 additions & 0 deletions frontend/app/components/user-create/UserCreateTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Center, Group, Title } from '@mantine/core';
import { RiUserAddFill } from 'react-icons/ri';

const UserCreateTitle = () => {
return (
<Center>
<Group justify="center" align="center">
<RiUserAddFill size="3.5ch" />
<Title order={1}>ユーザー作成</Title>
</Group>
</Center>
);
};

export default UserCreateTitle;
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { Button } from '@mantine/core';
import { RiUserAddFill } from 'react-icons/ri';

const UserAddButton = () => {
const UserCreateButton = () => {
return (
<Button
color="blue"
variant="filled"
radius="xl"
leftSection={<RiUserAddFill />}
component="a"
href="users/add"
href="users/create"
>
ユーザー追加
ユーザーを作成する
</Button>
);
};

export default UserAddButton;
export default UserCreateButton;
4 changes: 2 additions & 2 deletions frontend/app/components/users/UsersListComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import PaginationComponent from '../common/pagination/PaginationComponent';
import ErrorComponent from '../common/error/ErrorComponent';
import { getUsersResponse } from 'client/client';
import UsersListTable from './UsersListTable';
import UserAddButton from './UserAddButton';
import UserCreateButton from './UserCreateButton';

interface UsersListComponentProps {
paginationProps: PaginationProps;
Expand Down Expand Up @@ -42,7 +42,7 @@ const UsersListComponent = ({
) : (
<ErrorComponent message={'ユーザー情報を取得できませんでした。'} />
)}
<UserAddButton />
<UserCreateButton />
<PaginationComponent
total={paginationProps.total}
page={paginationProps.page}
Expand Down
Loading

0 comments on commit bddebba

Please sign in to comment.