Skip to content
Open
2 changes: 1 addition & 1 deletion user/src/api/fireEquipmentApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const API_ENDPOINTS = {
FIRE_EQUIPMENT_ORDERS: '/fire_equipment_orders',
};

// グループIDで火器使用申請を取得
// グループIDで火気使用申請を取得
export const useGetFireEquipmentOrderByGroupId = (
groupId: number | undefined
) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import '@globals';
import { Meta, StoryObj } from '@storybook/react';
import FireEquipment from './FireEquipment';

export default {
title: 'Components/FireEquipment',
tags: ['autodocs'],
component: FireEquipment,
parameters: {
docs: {
source: {
type: 'auto',
},
},
},
} as Meta<typeof FireEquipment>;

type Story = StoryObj<typeof FireEquipment>;

export const Default: Story = {
args: {
groupId: 0,
isDeadline: false,
isRegistered: false,
},
};
71 changes: 71 additions & 0 deletions user/src/components/Applications/FireEquipment/FireEquipment.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { FC } from 'react';
import AccordionMenu from '@/components/AccordionMenu';
import FormList from '@/components/FormList';
import { FireEquipmentFormView } from './components';
import { useFireEquipmentHooks } from './hooks';

type FireEquipmentProps = {
isDeadline?: boolean;
isRegistered?: boolean | undefined;
groupId: number;
};

const Content: FC<FireEquipmentProps> = ({ groupId, isDeadline }) => {
const {
isEditing,
handleEditClick,
formItem,
fireEquipment,
hasUnregistered,
noApplicationItems,
} = useFireEquipmentHooks(groupId);

// 締め切り後に表示する画面
if (isDeadline) {
return <FormList items={formItem} />;
}
// 未登録がある場合は、フォームリストを表示
if (hasUnregistered) {
return (
<FormList items={noApplicationItems} isEdit onEdit={handleEditClick} />
);
}

// 未登録の場合は、フォームを表示
if (!fireEquipment && !hasUnregistered) {
return <FireEquipmentFormView groupId={groupId} />;
}

// 編集モードでない場合は、フォームリストを表示
if (!isEditing) {
return <FormList items={formItem} isEdit onEdit={handleEditClick} />; // Display existing fire equipment details
}

// 編集モードの場合は、フォームを表示
return (
<FireEquipmentFormView
groupId={groupId}
fireEquipmentData={fireEquipment}
handleEditCancel={handleEditClick}
/>
);
};

const FireEquipment: FC<FireEquipmentProps> = ({
groupId,
isDeadline,
isRegistered,
}) => {
return (
<AccordionMenu
title={'火気使用申請'}
isEdit={!isDeadline}
isExist={isRegistered}
required={true}
>
<Content groupId={groupId} isDeadline={isDeadline} />
</AccordionMenu>
);
};

export default FireEquipment;
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { FC } from 'react';
import { FireEquipmentFuel } from '@/api/fireEquipmentApi';
import { FieldErrors, UseFormSetValue } from 'react-hook-form';
import Button from '@/components/Button';
import Radio from '@/components/Form/Radio/Radio';
import Selector from '@/components/Form/Selector/Selector';
import TextArea from '@/components/Form/TextArea/TextArea';
import TextBox from '@/components/Form/TextBox/TextBox';
import FormContainer from '@/components/FormContainer';
import {
FIRE_EQUIPMENT_FUEL_PLACEHOLDER_ID,
FIRE_EQUIPMENT_INSTRUCTIONS,
fireEquipmentFormFields,
} from '../constant';
import { convertToBoolToString } from './hooks';
import { FireEquipmentFormValues } from './schema';

type FireEquipmentFormProps = {
values: FireEquipmentFormValues;
errors: FieldErrors<FireEquipmentFormValues>;
setValue: UseFormSetValue<FireEquipmentFormValues>;
isEditing: boolean;
handleEditCancel?: () => void;
validate?: () => boolean;
};

const FireEquipmentForm: FC<FireEquipmentFormProps> = ({
values,
errors,
setValue,
isEditing,
handleEditCancel,
validate,
}) => {
return (
<FormContainer>
<div className="flex flex-col">
<div className="flex flex-col gap-10 text-[#484848]">
<TextBox
label={fireEquipmentFormFields.NAME}
required
value={values.name}
onChange={(value) => setValue('name', value)}
error={errors.name?.message}
/>
<TextBox
label={fireEquipmentFormFields.QUANTITY}
required
type="number"
value={values.quantity.toString()}
note="半角数字のみ"
onChange={(value) => setValue('quantity', Number(value))}
error={errors.quantity?.message}
/>
<Selector
label={fireEquipmentFormFields.FUEL}
required
options={[
{
id: FIRE_EQUIPMENT_FUEL_PLACEHOLDER_ID,
name: '選択してください',
disabled: true,
},
{
id: Number(FireEquipmentFuel.GAS_BOTTLE),
name: 'カセットガス',
disabled: false,
},
{
id: Number(FireEquipmentFuel.LP_GAS),
name: 'LPガス',
disabled: false,
},
{
id: Number(FireEquipmentFuel.CHARCOAL),
name: '炭',
disabled: false,
},
]}
value={values.fuel}
onChange={(value) =>
setValue('fuel', Number(value) as FireEquipmentFuel)
}
error={errors.fuel?.message}
/>
<TextArea
label={fireEquipmentFormFields.USAGE}
required
value={values.usage}
onChange={(value) => setValue('usage', value)}
error={errors.usage?.message}
/>
<Radio
label={fireEquipmentFormFields.IS_TAKEAWAY}
options={[
{ id: 1, name: 'はい' },
{ id: 2, name: 'いいえ' },
]}
value={convertToBoolToString(values.isTakeaway)}
onChange={(value) => {
setValue('isTakeaway', value === '1');
}}
required
error={errors.isTakeaway?.message}
/>
<p className="-mt-10 max-w-[400px] break-words text-xs text-[#484848]">
{FIRE_EQUIPMENT_INSTRUCTIONS.TAKEAWAY_NOTICE.split('\n').map(
(line, index) => (
<span key={index}>
{line}
{index === 0 && <br />}
</span>
)
)}
</p>
<p className="max-w-[400px] break-words text-xs text-[#484848]">
{FIRE_EQUIPMENT_INSTRUCTIONS.REMARK_NOTICE}
</p>
<TextArea
label={fireEquipmentFormFields.REMARK}
// 「火気を毎日テントから持ち帰ることができますか?」の質問で必須にするかしないか判定する
required
requireMessage="いいえを押した場合必須"
value={values.remarks || ''}
onChange={(value) => setValue('remarks', value)}
error={errors.remarks?.message}
/>
</div>
<div className="mt-10 flex w-full items-center justify-center">
{isEditing && (
<div className="mr-4">
<Button
size="pc"
color="main"
variant
type="button"
onClick={handleEditCancel}
>
キャンセル
</Button>
</div>
)}
<Button
size="pc"
color="main"
type="submit"
isDisable={validate && validate()}
>
{isEditing ? '修正' : '登録'}
</Button>
</div>
</div>
</FormContainer>
);
};

export default FireEquipmentForm;
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { FC } from 'react';
import { FireEquipmentResponse } from '@/api/fireEquipmentApi';
import { NO_ID_STRING, RADIO_OPTIONS, YES_ID_STRING } from '@/utils/constant';
import Button from '@/components/Button/Button';
import Radio from '@/components/Form/Radio/Radio';
import FireEquipmentForm from './FireEquipmentForm';
import { useFireEquipmentOrder } from './hooks';

type FireEquipmentFormViewProps = {
groupId: number;
fireEquipmentData?: FireEquipmentResponse;
handleEditCancel?: () => void;
};
export const FireEquipmentFormView: FC<FireEquipmentFormViewProps> = ({
groupId,
fireEquipmentData,
handleEditCancel,
}) => {
const {
isRegister,
submitUnregisteredHandler,
errorsUnregistered,
isRegisterValue,
setIsRegisterValue,
values,
errors,
setValue,
submitHandler,
isEditing,
validate,
} = useFireEquipmentOrder(groupId, fireEquipmentData);

return (
<div className="flex flex-col gap-6">
<form onSubmit={submitUnregisteredHandler}>
<Radio
label="火気申請を使用しますか?"
value={isRegisterValue}
onChange={(value) => {
setIsRegisterValue(
value as typeof YES_ID_STRING | typeof NO_ID_STRING
);
}}
required
options={RADIO_OPTIONS}
error={errorsUnregistered.isRegister?.message}
/>
<p className="max-w-[400px] break-words text-xs text-[#484848]">
電気ホットプレートとIHは含まれません。
</p>
{!isRegister && (
<div className="mt-8 flex flex-col items-center gap-4">
<Button type="submit" size="pc" color="main">
登録
</Button>
</div>
)}
</form>
{isRegister && (
<form onSubmit={submitHandler}>
<div className="flex w-full flex-col gap-10">
<FireEquipmentForm
values={values}
errors={errors}
setValue={setValue}
isEditing={isEditing}
handleEditCancel={handleEditCancel}
validate={validate}
/>
</div>
</form>
)}
</div>
);
};
Loading