Skip to content

Commit

Permalink
feat: 도시 추가 모달 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
LJW25 committed Feb 1, 2024
1 parent 6a4c1ce commit 8f9faf4
Show file tree
Hide file tree
Showing 20 changed files with 564 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { CityFormData } from '@type/city';

import { END_POINTS } from '@constants/api';

export const postTrip = async (cityFormData: CityFormData) => {
export const postCity = async (cityFormData: CityFormData) => {
const response = await axiosInstance.post(END_POINTS.CITY, cityFormData);

const tripId = response.headers.location.replace(`${END_POINTS.CITY}/`, '');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import type { CityFormData } from '@type/city';
import { END_POINTS } from '@constants/api';

export interface PutCityParams extends CityFormData {
cityId: string;
cityId: number;
}

export const putTrip = ({ cityId, ...cityInformation }: PutCityParams) => {
export const putCity = ({ cityId, ...cityInformation }: PutCityParams) => {
return axiosInstance.put<CityFormData>(END_POINTS.CHANGE_CITY(cityId), {
...cityInformation,
});
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { css } from '@emotion/react';

import { Theme } from 'hang-log-design-system';

export const wrapperStyling = css({
width: '400px',
minHeight: '528px',

'@media screen and (max-width: 600px)': {
width: `calc(100vw - ${Theme.spacer.spacing4})`,
height: `80%`,
},
});

export const formStyling = css({
display: 'flex',
flexDirection: 'column',
gap: Theme.spacer.spacing4,
width: '80%',
});

export const buttonStyling = css({
width: '100%',
});

export const closeButtonStyling = css({
position: 'absolute',
right: Theme.spacer.spacing4,
top: Theme.spacer.spacing4,
alignSelf: 'flex-end',

marginBottom: Theme.spacer.spacing1,

border: 'none',
backgroundColor: 'transparent',

cursor: 'pointer',
});

export const closeIconStyling = css({
width: '16px',
height: '16px',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { Button, Flex, Modal, Theme } from 'hang-log-design-system';

import { UseAddCityForm } from '@/hooks/city/useAddCityForm';

import type { CityFormData } from '@/types/city';

import CloseIcon from '@assets/svg/close-icon.svg?react';

import NameInput from './NameInput/NameInput';
import CountryInput from './CountryInput/CountryInput';
import LatitudeInput from './LatitudeInput/LatitudeInput';
import LongitudeInput from './LongitudeInput/LongitudeInput';

import {
wrapperStyling,
formStyling,
buttonStyling,
closeButtonStyling,
closeIconStyling,
} from './CityAddModal.style';

interface CityAddModalProps {
cityId?: number;
initialData?: CityFormData;
isOpen?: boolean;
onClose: () => void;
}

const CityAddModal = ({ cityId, initialData, isOpen = true, onClose }: CityAddModalProps) => {
const {
cityInformation,
isNameError,
isCountryError,
isLatitudeError,
isLongitudeError,
disableNameError,
disableCountryError,
disableLatitudeError,
disableLongitudeError,
updateInputValue,
handleSubmit,
} = UseAddCityForm({
initialData,
onSuccess: onClose,
});

return (
<>
<Modal
css={wrapperStyling}
isOpen={isOpen}
closeModal={onClose}
isBackdropClosable={false}
hasCloseButton={false}
>
<button
type="button"
aria-label="모달 닫기 버튼"
onClick={onClose}
css={closeButtonStyling}
>
<CloseIcon css={closeIconStyling} />
</button>
<form css={formStyling} onSubmit={handleSubmit} noValidate>
<Flex styles={{ direction: 'column', gap: Theme.spacer.spacing3, align: 'stretch' }}>
<NameInput
value={cityInformation.name}
isError={isNameError}
updateInputValue={updateInputValue}
disableError={disableNameError}
/>
<CountryInput
value={cityInformation.country}
isError={isCountryError}
updateInputValue={updateInputValue}
disableError={disableCountryError}
/>
<LatitudeInput
value={cityInformation.latitude}
isError={isLatitudeError}
updateInputValue={updateInputValue}
disableError={disableLatitudeError}
/>
<LongitudeInput
value={cityInformation.longitude}
isError={isLongitudeError}
updateInputValue={updateInputValue}
disableError={disableLongitudeError}
/>
</Flex>
<Button css={buttonStyling} variant="primary">
도시 {cityId ? '수정하기' : '추가하기'}
</Button>
</form>
</Modal>
</>
);
};

export default CityAddModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { ChangeEvent } from 'react';
import { memo } from 'react';

import { Input } from 'hang-log-design-system';

import type { CityFormData } from '@/types/city';

import { CITY_COUNTRY_MAX_LENGTH } from '@/constants/ui';

interface CountryInputProps {
value: string;
isError: boolean;
updateInputValue: <K extends keyof CityFormData>(key: K, value: CityFormData[K]) => void;
disableError: () => void;
}

const CountryInput = ({ isError, value, updateInputValue, disableError }: CountryInputProps) => {
const handleCountryChange = (event: ChangeEvent<HTMLInputElement>) => {
disableError();

updateInputValue('country', event.target.value);
};

return (
<Input
label="나라"
id="country"
name="country"
maxLength={CITY_COUNTRY_MAX_LENGTH}
value={value}
placeholder="나라를 입력해 주세요"
supportingText={isError ? '도시의 나라를 입력해 주세요' : undefined}
isError={isError}
required
onChange={handleCountryChange}
/>
);
};

export default memo(CountryInput);
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { ChangeEvent } from 'react';
import { memo } from 'react';

import { Input } from 'hang-log-design-system';

import type { CityFormData } from '@/types/city';

import { CITY_LATITUDE_MAX, CITY_LATITUDE_MIN } from '@/constants/ui';

interface LatitudeInputProps {
value: number;
isError: boolean;
updateInputValue: <K extends keyof CityFormData>(key: K, value: CityFormData[K]) => void;
disableError: () => void;
}

const LatitudeInput = ({ isError, value, updateInputValue, disableError }: LatitudeInputProps) => {
const handleLatitudeChange = (event: ChangeEvent<HTMLInputElement>) => {
disableError();

updateInputValue('latitude', Number(event.target.value));
};

return (
<Input
type="number"
label="위도"
id="latitude"
name="latitude"
value={value}
placeholder="0.0"
min={CITY_LATITUDE_MIN}
max={CITY_LATITUDE_MAX}
supportingText={isError ? '도시의 위도를 입력해 주세요' : undefined}
isError={isError}
required
onChange={handleLatitudeChange}
/>
);
};

export default memo(LatitudeInput);
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { ChangeEvent } from 'react';
import { memo } from 'react';

import { Input } from 'hang-log-design-system';

import type { CityFormData } from '@/types/city';

import { CITY_LONGITUDE_MAX, CITY_LONGITUDE_MIN } from '@/constants/ui';

interface LongitudeInputProps {
value: number;
isError: boolean;
updateInputValue: <K extends keyof CityFormData>(key: K, value: CityFormData[K]) => void;
disableError: () => void;
}

const LongitudeInput = ({
isError,
value,
updateInputValue,
disableError,
}: LongitudeInputProps) => {
const handleLongitudeChange = (event: ChangeEvent<HTMLInputElement>) => {
disableError();

updateInputValue('longitude', Number(event.target.value));
};

return (
<Input
type="number"
label="경도"
id="longitude"
name="longitude"
value={value}
placeholder="0.0"
min={CITY_LONGITUDE_MIN}
max={CITY_LONGITUDE_MAX}
supportingText={isError ? '도시의 경도를 입력해 주세요' : undefined}
isError={isError}
required
onChange={handleLongitudeChange}
/>
);
};

export default memo(LongitudeInput);
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { ChangeEvent } from 'react';
import { memo } from 'react';

import { Input } from 'hang-log-design-system';

import type { CityFormData } from '@/types/city';

import { CITY_NAME_MAX_LENGTH } from '@/constants/ui';

interface NameInputProps {
value: string;
isError: boolean;
updateInputValue: <K extends keyof CityFormData>(key: K, value: CityFormData[K]) => void;
disableError: () => void;
}

const NameInput = ({ isError, value, updateInputValue, disableError }: NameInputProps) => {
const handleTitleChange = (event: ChangeEvent<HTMLInputElement>) => {
disableError();

updateInputValue('name', event.target.value);
};

return (
<Input
label="이름"
id="name"
name="name"
maxLength={CITY_NAME_MAX_LENGTH}
value={value}
placeholder="이름을 입력해 주세요"
supportingText={isError ? '도시의 이름을 입력해 주세요' : undefined}
isError={isError}
required
onChange={handleTitleChange}
/>
);
};

export default memo(NameInput);
8 changes: 4 additions & 4 deletions frontend-monorepo/packages/hanglog-admin/src/constants/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ export const END_POINTS = {
TOKEN: '/admin/token',
LOGOUT: '/admin/logout',
MEMBER: '/admin/members',
CHANGE_MEMBER_PASSWORD: (memberId: string) => `/admin/members/${memberId}/password`,
CHANGE_MEMBER_PASSWORD: (memberId: number) => `/admin/members/${memberId}/password`,
CITY: '/admin/cities',
CHANGE_CITY: (cityId: string) => `/admin/cities/${cityId}`,
CHANGE_CITY: (cityId: number) => `/admin/cities/${cityId}`,
CATEGORY: '/admin/categories',
CHANGE_CATEGORY: (categoryId: string) => `/admin/categories/${categoryId}`,
CHANGE_CATEGORY: (categoryId: number) => `/admin/categories/${categoryId}`,
CURRENCY: '/admin/currency',
CURRENCIES: (page: number, size: number) => `/admin/currencies/trips?page=${page}&size=${size}`,
CHANGE_CURRENCY: (currencyId: string) => `/admin/currencies/${currencyId}`,
CHANGE_CURRENCY: (currencyId: number) => `/admin/currencies/${currencyId}`,
} as const;

export const NETWORK = {
Expand Down
12 changes: 12 additions & 0 deletions frontend-monorepo/packages/hanglog-admin/src/constants/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,15 @@ export const MOBILE_MEDIA_QUERY_SIZE = '(max-width: 600px)';
export const INDEX_UNIT_LENGTH = 5;

export const TABLE_ROW_LENGTH = 10;

export const CITY_NAME_MAX_LENGTH = 20;

export const CITY_COUNTRY_MAX_LENGTH = 20;

export const CITY_LATITUDE_MIN = -90;

export const CITY_LATITUDE_MAX = 90;

export const CITY_LONGITUDE_MIN = -180;

export const CITY_LONGITUDE_MAX = 180;
Loading

0 comments on commit 8f9faf4

Please sign in to comment.