Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

강제 업데이트 네비게이션 생성 #61

Merged
merged 20 commits into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .yarn/cache/fsevents-patch-21ad2b1333-8.zip
Binary file not shown.
4 changes: 4 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import BenefitPage from 'pages/Services/Benefit';
import NoticeList from 'pages/Services/Notice/NoticeList';
import NoticeDetail from 'pages/Services/Notice/NoticeDetail';
import NoticeWrite from 'pages/Services/Notice/NoticeWrite';
import ForceUpdate from 'pages/Update/ForceUpdate';
import UpdateList from 'pages/Update/UpdateList';

function RequireAuth() {
const location = useLocation();
Expand Down Expand Up @@ -70,6 +72,8 @@ function App() {
<Route path="/notice" element={<NoticeList />} />
<Route path="/notice/:id" element={<NoticeDetail />} />
<Route path="/notice/write" element={<NoticeWrite />} />
<Route path="/force-update" element={<ForceUpdate />} />
<Route path="/update-list" element={<UpdateList />} />
<Route path="*" element={<h1>404</h1>} />
</Route>
</Routes>
Gwak-Seungju marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
18 changes: 16 additions & 2 deletions src/components/common/CustomForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,30 @@ function CustomInputNumber({
);
}

interface AutoSizeProps {
minRows: number,
maxRows: number,
}

interface CustomTextAreaProps {
maxLength?: number;
showCount?: boolean;
style?: React.CSSProperties;
autoSize?: AutoSizeProps;
}

function CusctomTextArea({
label, name, maxLength, disabled, rules,
label, name, maxLength, disabled, rules, showCount, autoSize, style,
}: CustomFormItemProps & CustomTextAreaProps) {
return (
<S.FormItem label={label} name={name} rules={rules}>
<Input.TextArea showCount maxLength={maxLength} disabled={disabled} />
<Input.TextArea
showCount={showCount}
maxLength={maxLength}
disabled={disabled}
autoSize={autoSize}
style={style}
/>
</S.FormItem>
);
}
Expand Down
7 changes: 6 additions & 1 deletion src/components/common/SideNav/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
HomeOutlined, UserSwitchOutlined,
UsergroupDeleteOutlined, FolderOpenOutlined, ControlOutlined,
UserAddOutlined, BoldOutlined, ApartmentOutlined, SnippetsOutlined, GiftOutlined,
NotificationOutlined,
NotificationOutlined, IssuesCloseOutlined, FormOutlined, UnorderedListOutlined,
} from '@ant-design/icons';
import { Menu, MenuProps } from 'antd';
import { Link, useLocation, useNavigate } from 'react-router-dom';
Expand Down Expand Up @@ -49,6 +49,11 @@ const items: MenuProps['items'] = [
getItem('테스트', 'test', <ControlOutlined />, [
getItem('AB 테스트', '/abtest', <ApartmentOutlined />),
]),

getItem('강제업데이트', 'force-update', <IssuesCloseOutlined />, [
getItem('업데이트 관리', '/force-update', <FormOutlined />),
getItem('목록 관리', '/update-list', <UnorderedListOutlined />),
]),
];

const SideNavConatiner = styled.nav`
Gwak-Seungju marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
1 change: 1 addition & 0 deletions src/constant/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ export const TITLE_MAPPER: Record<string, string> = {
title: '제목',
author: '작성자',
post_date: '게시일',
updatedAt: 'date',
};
Gwak-Seungju marked this conversation as resolved.
Show resolved Hide resolved
18 changes: 18 additions & 0 deletions src/model/forceUpdate.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export type OS = 'android' | 'ios';

export interface UpdateAppVersionRequest {
type: OS;
version: string;
title: string;
content: string;
}

export interface AppVersionResponse {
id: number;
type: OS;
version: string;
title: string;
content: string;
created_at: string;
updated_at: string;
}
Gwak-Seungju marked this conversation as resolved.
Show resolved Hide resolved
15 changes: 15 additions & 0 deletions src/model/updateList.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { OS, AppVersionResponse } from 'model/forceUpdate.model';

export interface UpdateListRequest {
page: number;
type: OS;
limit?: number;
}

export interface UpdateListResponse {
total_count: number;
current_count: number;
total_page: number;
current_page: number;
versions: AppVersionResponse[];
}
Gwak-Seungju marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions src/pages/Update/ForceUpdate/ForceUpdate.style.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from 'styles/List.style';
129 changes: 129 additions & 0 deletions src/pages/Update/ForceUpdate/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { message, Divider, Flex } from 'antd';
import { useEffect, useState } from 'react';
import CustomForm from 'components/common/CustomForm';
import { OS, UpdateAppVersionRequest } from 'model/forceUpdate.model';
import { useGetAppVersionQuery, useUpdateAppVersionMutation } from 'store/api/forceUpdate';
import OSDropdown from 'pages/Update/components/OSDropdown';
import * as S from './ForceUpdate.style';

const versionRegex = /^\d+\.\d+\.\d+$/;

const titleStyle = {
fontSize: '20px',
fontWeight: '700',
};

const inputStyle = {
fontSize: '14px',
fontWeight: '700',
color: '#000',
padding: '18px 12px',
whiteSpace: 'nowrap',
maxWidth: '100%',
};

const textAreaStyle = {
fontSize: '14px',
fontWeight: '700',
color: '#000',
borderRadius: '0',
backgroundColor: '#fff',
padding: '18px 12px',
};

export default function ForceUpdate() {
const [os, setOs] = useState<OS>('android');

const [currentVersionForm] = CustomForm.useForm();
const [afterVersionForm] = CustomForm.useForm();
const { pattern, required } = CustomForm.useValidate();

const { data: version } = useGetAppVersionQuery(os);
const [updateVersion] = useUpdateAppVersionMutation();

const handleOS = (type: OS) => {
setOs(type);
};

const onFinish = (formData: UpdateAppVersionRequest) => {
updateVersion({
type: os,
version: formData.version,
title: formData.title,
content: formData.content,
})
.then(() => {
afterVersionForm.setFieldsValue({
version: '',
title: '',
content: '',
});
message.success('업데이트 완료');
})
.catch(({ data }) => {
message.error(data.message);
});
};

useEffect(() => {
currentVersionForm.setFieldsValue(version);
}, [currentVersionForm, version]);

return (
<Flex vertical>
<S.Heading>강제 업데이트 관리</S.Heading>
<OSDropdown
os={os}
handleOS={handleOS}
/>
{version && (
<CustomForm form={currentVersionForm} initialValues={version}>
<Divider
orientation="left"
orientationMargin="0"
style={titleStyle}
>
현재 업데이트 상황
</Divider>
<CustomForm.TextArea label="version" name="version" disabled showCount={false} style={textAreaStyle} autoSize={{ minRows: 1, maxRows: 5 }} />
<CustomForm.TextArea label="title" name="title" disabled showCount={false} style={textAreaStyle} autoSize={{ minRows: 1, maxRows: 5 }} />
<CustomForm.TextArea label="content" name="content" disabled showCount={false} style={textAreaStyle} autoSize={{ minRows: 1, maxRows: 5 }} />
</CustomForm>
)}
<Divider />
<CustomForm form={afterVersionForm} onFinish={onFinish}>
<Divider
orientation="left"
orientationMargin="0"
style={titleStyle}
>
수정 문구는 아래에 입력해서 수정해주세요.
</Divider>
<CustomForm.Input
label="version"
name="version"
rules={[pattern(versionRegex, '예시 형식과 맞게 version을 입력해주세요.'), required()]}
placeholder="ex) 1.2.0"
style={inputStyle}
/>
<CustomForm.Input
label="title"
name="title"
rules={[required()]}
placeholder="변경할 코인업데이트 화면 제목 문구를 작성해주세요."
style={inputStyle}
/>
<CustomForm.Input
label="content"
name="content"
rules={[required()]}
placeholder="변경할 코인업데이트 화면 콘텐츠 문구를 작성해주세요."
style={inputStyle}
/>
<Flex justify="end">
<CustomForm.Button htmlType="submit">수정 완료</CustomForm.Button>
</Flex>
</CustomForm>
</Flex>
);
}
1 change: 1 addition & 0 deletions src/pages/Update/UpdateList/UpdateList.style.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from 'styles/List.style';
44 changes: 44 additions & 0 deletions src/pages/Update/UpdateList/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Flex } from 'antd';
import OSDropdown from 'pages/Update/components/OSDropdown';
import { useState } from 'react';
import { OS } from 'model/forceUpdate.model';
import CustomTable from 'components/common/CustomTable';
import { useGetUpdateListQuery } from 'store/api/updateList';
import * as S from './UpdateList.style';

export default function UpdateList() {
const [os, setOs] = useState<OS>('android');

const [page, setPage] = useState<number>(1);

const { data: updateList } = useGetUpdateListQuery({ page, type: os });

const handleOS = (type: OS) => {
setOs(type);
};

return (
<Flex vertical>
<S.Heading>강제 업데이트 목록</S.Heading>
<OSDropdown
os={os}
handleOS={handleOS}
/>
{updateList
&& (
<CustomTable
data={updateList.versions}
pagination={{
current: page,
onChange: setPage,
total: updateList.total_page,
}}
columnSize={[10, 10, 30, 30, 10]}
hiddenColumns={['id', 'createdAt']}
onClick={() => {}}
/>
)}

</Flex>
);
}
Gwak-Seungju marked this conversation as resolved.
Show resolved Hide resolved
42 changes: 42 additions & 0 deletions src/pages/Update/components/OSDropdown.style.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import styled from 'styled-components';

export const TypeContainer = styled.div`
position: relative;
width: 150px;
height: 50px;
align-self: end;
`;

export const Type = styled.button`
position: relative;
width: 100%;
height: 100%;
background-color: #fff;
font-weight: 700;
`;

export const Icon = styled.span`
position: absolute;
right: 10px;
`;

export const MenuList = styled.ul`
position: absolute;
top: 50px;
left: 0;
padding: 0;
margin: 0;
font-weight: 700;
list-style-type: none;
z-index: 5;
background-color: #fff;
`;

export const Menu = styled.li`
display: flex;
align-items: center;
justify-content: center;
width: 150px;
height: 50px;
border: 1px solid #000;
`;
44 changes: 44 additions & 0 deletions src/pages/Update/components/OSDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useState } from 'react';
import { OS } from 'model/forceUpdate.model';
import { DownOutlined, UpOutlined } from '@ant-design/icons';
import * as S from './OSDropdown.style';

interface OSDropdownProps {
os: OS;
handleOS: (type: OS) => void;
}

export default function OSDropdown({ os, handleOS }: OSDropdownProps) {
const [isOpen, setIsOpen] = useState(false);

const osList: OS[] = ['android', 'ios'];

const toggle = () => {
setIsOpen((prev) => !prev);
};

const selectOS = (type: OS) => {
handleOS(type);
toggle();
};

return (
<S.TypeContainer>
<S.Type onClick={toggle}>
{os}
<S.Icon>{isOpen ? <UpOutlined /> : <DownOutlined />}</S.Icon>
</S.Type>
{isOpen && (
<S.MenuList>
{osList.map(
(type) => os !== type && (
<S.Menu onClick={() => selectOS(type)} key={type}>
{type}
</S.Menu>
),
)}
</S.MenuList>
)}
</S.TypeContainer>
);
}
Loading
Loading