+++ src/admin/component/checkbox/CheckBox.tsx
... | ... | @@ -0,0 +1,54 @@ |
| 1 | +import {type ChangeEvent, useEffect, useRef} from "react"; | |
| 2 | + | |
| 3 | +type CheckBoxProps = { | |
| 4 | + id: string; | |
| 5 | + name: string; | |
| 6 | + checked: boolean; | |
| 7 | + onChange: (checked: boolean) => void; | |
| 8 | + value?: string; | |
| 9 | + label?: string; | |
| 10 | + disabled?: boolean; | |
| 11 | + indeterminate?: boolean; | |
| 12 | +}; | |
| 13 | + | |
| 14 | +export const CheckBox = ({ | |
| 15 | + id, | |
| 16 | + name, | |
| 17 | + checked, | |
| 18 | + onChange, | |
| 19 | + value, | |
| 20 | + label, | |
| 21 | + disabled = false, | |
| 22 | + indeterminate = false, | |
| 23 | + }: CheckBoxProps) => { | |
| 24 | + const inputRef = useRef<HTMLInputElement>(null); | |
| 25 | + | |
| 26 | + useEffect(() => { | |
| 27 | + if (!inputRef.current) { | |
| 28 | + return; | |
| 29 | + } | |
| 30 | + | |
| 31 | + inputRef.current.indeterminate = indeterminate; | |
| 32 | + }, [indeterminate]); | |
| 33 | + | |
| 34 | + const handleChange = (event: ChangeEvent<HTMLInputElement>) => { | |
| 35 | + onChange(event.target.checked); | |
| 36 | + }; | |
| 37 | + | |
| 38 | + return ( | |
| 39 | + <> | |
| 40 | + <input | |
| 41 | + ref={inputRef} | |
| 42 | + type="checkbox" | |
| 43 | + className="checkbox" | |
| 44 | + id={id} | |
| 45 | + name={name} | |
| 46 | + value={value} | |
| 47 | + checked={checked} | |
| 48 | + disabled={disabled} | |
| 49 | + onChange={handleChange} | |
| 50 | + /> | |
| 51 | + <label htmlFor={id}>{label}</label> | |
| 52 | + </> | |
| 53 | + ); | |
| 54 | +}; |
--- src/admin/feature/board/api/boardApi.ts
+++ src/admin/feature/board/api/boardApi.ts
... | ... | @@ -4,6 +4,8 @@ |
| 4 | 4 |
BoardArticleExtra, |
| 5 | 5 |
BoardArticleListItem, |
| 6 | 6 |
BoardArticleSearchParams, |
| 7 |
+ BoardDetailResponse, |
|
| 8 |
+ BoardFormItem, |
|
| 7 | 9 |
BoardListItem, |
| 8 | 10 |
BoardSearchParams |
| 9 | 11 |
} from "../type/board.types.ts"; |
... | ... | @@ -15,3 +17,23 @@ |
| 15 | 17 |
export async function fetchBoardArticleList(params: BoardArticleSearchParams) {
|
| 16 | 18 |
return apiClient.get<PageResponse<BoardArticleListItem, BoardArticleExtra>>('/cop/bbs/boardList.do', params);
|
| 17 | 19 |
} |
| 20 |
+ |
|
| 21 |
+export async function fetchBoardDetail(bbsId: string) {
|
|
| 22 |
+ return apiClient.get<BoardDetailResponse>(`/cop/bbs/detail.do?bbsId=${bbsId}`);
|
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+export async function createBoard(params: BoardFormItem) {
|
|
| 26 |
+ return apiClient.post(`/cop/bbs/insertBoardMasater.do`, params); |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+export async function updateBoard(params: BoardFormItem) {
|
|
| 30 |
+ return apiClient.post(`/cop/bbs/updateBoardMaster.do`, params); |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+export async function deleteBoard(bbsId: string) {
|
|
| 34 |
+ return apiClient.post(`/cop/bbs/deleteBoardMaster.do?bbsId=${bbsId}`);
|
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+export async function deleteBoardBatch() {
|
|
| 38 |
+ return apiClient.post(`/cop/bbs/deleteBoardBatch.do`, {});
|
|
| 39 |
+} |
--- src/admin/feature/board/components/article/BoardArticleListTable.tsx
+++ src/admin/feature/board/components/article/BoardArticleListTable.tsx
... | ... | @@ -8,6 +8,11 @@ |
| 8 | 8 |
items: BoardArticleListItem[]; |
| 9 | 9 |
params: BoardArticleSearchParams; |
| 10 | 10 |
onChange: (params: BoardArticleSearchParams) => void; |
| 11 |
+ isAllChecked: boolean; |
|
| 12 |
+ isPartiallyChecked: boolean; |
|
| 13 |
+ isChecked: (id: string) => boolean; |
|
| 14 |
+ onCheck: (id: string, checked: boolean) => void; |
|
| 15 |
+ onCheckAll: (checked: boolean) => void; |
|
| 11 | 16 |
totalItems: number |
| 12 | 17 |
currentPage: number |
| 13 | 18 |
totalPages: number |
... | ... | @@ -17,6 +22,11 @@ |
| 17 | 22 |
items, |
| 18 | 23 |
params, |
| 19 | 24 |
onChange, |
| 25 |
+ isAllChecked, |
|
| 26 |
+ isPartiallyChecked, |
|
| 27 |
+ isChecked, |
|
| 28 |
+ onCheck, |
|
| 29 |
+ onCheckAll, |
|
| 20 | 30 |
totalItems, |
| 21 | 31 |
currentPage, |
| 22 | 32 |
totalPages |
... | ... | @@ -28,6 +38,9 @@ |
| 28 | 38 |
<BoardArticleListTableHeader |
| 29 | 39 |
params={params}
|
| 30 | 40 |
onChange={onChange}
|
| 41 |
+ checked={isAllChecked}
|
|
| 42 |
+ indeterminate={isPartiallyChecked}
|
|
| 43 |
+ onCheckAll={onCheckAll}
|
|
| 31 | 44 |
/> |
| 32 | 45 |
<tbody> |
| 33 | 46 |
{items.length > 0 ?
|
... | ... | @@ -40,6 +53,8 @@ |
| 40 | 53 |
totalItems={totalItems}
|
| 41 | 54 |
currentPage={currentPage}
|
| 42 | 55 |
totalPages={totalPages}
|
| 56 |
+ checked={isChecked(item.nttId)}
|
|
| 57 |
+ onCheck={onCheck}
|
|
| 43 | 58 |
/> |
| 44 | 59 |
)) : |
| 45 | 60 |
(<EmptyRow colSpan={8}/>)
|
... | ... | @@ -49,4 +64,4 @@ |
| 49 | 64 |
</div> |
| 50 | 65 |
) |
| 51 | 66 |
|
| 52 |
-}(No newline at end of file) |
|
| 67 |
+} |
--- src/admin/feature/board/components/article/BoardArticleListTableHeader.tsx
+++ src/admin/feature/board/components/article/BoardArticleListTableHeader.tsx
... | ... | @@ -1,13 +1,20 @@ |
| 1 | 1 |
import type {BoardArticleSearchParams} from "../../type/board.types.ts";
|
| 2 |
+import {CheckBox} from "../../../../component/checkbox/CheckBox.tsx";
|
|
| 2 | 3 |
|
| 3 | 4 |
type BoardArticleListTableHeaderProps = {
|
| 4 | 5 |
params: BoardArticleSearchParams; |
| 5 | 6 |
onChange: (params: BoardArticleSearchParams) => void; |
| 7 |
+ checked: boolean; |
|
| 8 |
+ indeterminate: boolean; |
|
| 9 |
+ onCheckAll: (checked: boolean) => void; |
|
| 6 | 10 |
} |
| 7 | 11 |
|
| 8 | 12 |
export const BoardArticleListTableHeader = ({
|
| 9 | 13 |
params, |
| 10 |
- onChange |
|
| 14 |
+ onChange, |
|
| 15 |
+ checked, |
|
| 16 |
+ indeterminate, |
|
| 17 |
+ onCheckAll, |
|
| 11 | 18 |
}: BoardArticleListTableHeaderProps) => {
|
| 12 | 19 |
const handleSort = (field: string) => {
|
| 13 | 20 |
const nextOrder = |
... | ... | @@ -52,8 +59,13 @@ |
| 52 | 59 |
<thead> |
| 53 | 60 |
<tr> |
| 54 | 61 |
<th> |
| 55 |
- <input type={"checkbox"} name={"checkbox"}/>
|
|
| 56 |
- <label htmlFor={"checkbox"}></label>
|
|
| 62 |
+ <CheckBox |
|
| 63 |
+ id="checkAll" |
|
| 64 |
+ name="checkAll" |
|
| 65 |
+ checked={checked}
|
|
| 66 |
+ indeterminate={indeterminate}
|
|
| 67 |
+ onChange={onCheckAll}
|
|
| 68 |
+ /> |
|
| 57 | 69 |
</th> |
| 58 | 70 |
<th>번호</th> |
| 59 | 71 |
<th scope={"col"}>
|
... | ... | @@ -79,4 +91,4 @@ |
| 79 | 91 |
</thead> |
| 80 | 92 |
</> |
| 81 | 93 |
) |
| 82 |
-}(No newline at end of file) |
|
| 94 |
+} |
--- src/admin/feature/board/components/article/BoardArticleListTableRow.tsx
+++ src/admin/feature/board/components/article/BoardArticleListTableRow.tsx
... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 |
import type {BoardArticleListItem} from "../../type/board.types.ts";
|
| 2 | 2 |
import type {SearchParams} from "../../../../../type/searchParams.ts";
|
| 3 |
+import {CheckBox} from "../../../../component/checkbox/CheckBox.tsx";
|
|
| 3 | 4 |
|
| 4 | 5 |
type BoardArticleListTableRowProps = {
|
| 5 | 6 |
item: BoardArticleListItem |
... | ... | @@ -8,6 +9,8 @@ |
| 8 | 9 |
totalItems: number |
| 9 | 10 |
currentPage: number |
| 10 | 11 |
totalPages: number |
| 12 |
+ checked: boolean |
|
| 13 |
+ onCheck: (id: string, checked: boolean) => void |
|
| 11 | 14 |
} |
| 12 | 15 |
|
| 13 | 16 |
export const BoardArticleListTableRow = ({
|
... | ... | @@ -17,6 +20,8 @@ |
| 17 | 20 |
totalItems, |
| 18 | 21 |
currentPage, |
| 19 | 22 |
totalPages, |
| 23 |
+ checked, |
|
| 24 |
+ onCheck, |
|
| 20 | 25 |
}: BoardArticleListTableRowProps) => {
|
| 21 | 26 |
|
| 22 | 27 |
const rowNumber = searchParams.searchSortOrd === 'DESC' |
... | ... | @@ -24,7 +29,15 @@ |
| 24 | 29 |
: (currentPage - 1) * totalPages + (index + 1) |
| 25 | 30 |
return ( |
| 26 | 31 |
<tr> |
| 27 |
- <td></td> |
|
| 32 |
+ <td> |
|
| 33 |
+ <CheckBox |
|
| 34 |
+ id={`checkList_${item.nttId}`}
|
|
| 35 |
+ name="checkList" |
|
| 36 |
+ value={item.nttId}
|
|
| 37 |
+ checked={checked}
|
|
| 38 |
+ onChange={(nextChecked) => onCheck(item.nttId, nextChecked)}
|
|
| 39 |
+ /> |
|
| 40 |
+ </td> |
|
| 28 | 41 |
<td>{rowNumber}</td>
|
| 29 | 42 |
<td>{item.nttSj}</td>
|
| 30 | 43 |
<td>{item.atchFileId}</td>
|
... | ... | @@ -34,4 +47,4 @@ |
| 34 | 47 |
<td>{item.inqireCo}</td>
|
| 35 | 48 |
</tr> |
| 36 | 49 |
) |
| 37 |
-}(No newline at end of file) |
|
| 50 |
+} |
+++ src/admin/feature/board/components/master/BoardFormTable.tsx
... | ... | @@ -0,0 +1,193 @@ |
| 1 | +import type {ChangeEvent} from "react"; | |
| 2 | +import type {BoardFormItem} from "../../type/board.types.ts"; | |
| 3 | +import type {CommonCodeItem} from "../../../../../type/code.ts"; | |
| 4 | + | |
| 5 | +type BoardFormTableProps = { | |
| 6 | + form: BoardFormItem; | |
| 7 | + typeList?: CommonCodeItem[]; | |
| 8 | + onChange: (event: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => void; | |
| 9 | +}; | |
| 10 | + | |
| 11 | +export const BoardFormTable = ({ | |
| 12 | + form, | |
| 13 | + typeList = [], | |
| 14 | + onChange | |
| 15 | + }: BoardFormTableProps) => { | |
| 16 | + return ( | |
| 17 | + <div className="table table_type_rows"> | |
| 18 | + <table> | |
| 19 | + <colgroup> | |
| 20 | + <col style={{ width: '200px' }} /> | |
| 21 | + <col style={{ width: 'auto' }} /> | |
| 22 | + </colgroup> | |
| 23 | + | |
| 24 | + <tbody> | |
| 25 | + <tr> | |
| 26 | + <th> | |
| 27 | + <span className="required">*</span> | |
| 28 | + 게시판명 | |
| 29 | + </th> | |
| 30 | + <td> | |
| 31 | + <input | |
| 32 | + type="text" | |
| 33 | + className="input" | |
| 34 | + id="bbsNm" | |
| 35 | + name="bbsNm" | |
| 36 | + value={form.bbsNm} | |
| 37 | + onChange={onChange} | |
| 38 | + /> | |
| 39 | + </td> | |
| 40 | + </tr> | |
| 41 | + | |
| 42 | + <tr> | |
| 43 | + <th> | |
| 44 | + <span className="required">*</span> | |
| 45 | + 게시판유형 | |
| 46 | + </th> | |
| 47 | + <td> | |
| 48 | + {typeList.map((type) => ( | |
| 49 | + <label key={type.code}> | |
| 50 | + <input | |
| 51 | + type="radio" | |
| 52 | + id={`bbsTyCode_${type.code}`} | |
| 53 | + name="bbsTyCode" | |
| 54 | + value={type.code} | |
| 55 | + checked={form.bbsTyCode === type.code} | |
| 56 | + onChange={onChange} | |
| 57 | + /> | |
| 58 | + {type.codeNm} | |
| 59 | + </label> | |
| 60 | + ))} | |
| 61 | + </td> | |
| 62 | + </tr> | |
| 63 | + | |
| 64 | + <tr> | |
| 65 | + <th> | |
| 66 | + <span className="required">*</span> | |
| 67 | + 공지기능 | |
| 68 | + </th> | |
| 69 | + <td> | |
| 70 | + <select | |
| 71 | + id="noticeYn" | |
| 72 | + name="noticeYn" | |
| 73 | + value={form.noticeYn} | |
| 74 | + onChange={onChange} | |
| 75 | + > | |
| 76 | + <option value="Y">사용</option> | |
| 77 | + <option value="N">미사용</option> | |
| 78 | + </select> | |
| 79 | + </td> | |
| 80 | + </tr> | |
| 81 | + | |
| 82 | + <tr> | |
| 83 | + <th> | |
| 84 | + <span className="required">*</span> | |
| 85 | + 댓글기능 | |
| 86 | + </th> | |
| 87 | + <td> | |
| 88 | + <select | |
| 89 | + id="addYn" | |
| 90 | + name="addYn" | |
| 91 | + value={form.addYn} | |
| 92 | + onChange={onChange} | |
| 93 | + > | |
| 94 | + <option value="Y">사용</option> | |
| 95 | + <option value="N">미사용</option> | |
| 96 | + </select> | |
| 97 | + </td> | |
| 98 | + </tr> | |
| 99 | + | |
| 100 | + <tr> | |
| 101 | + <th> | |
| 102 | + <span className="required">*</span> | |
| 103 | + 파일첨부기능 | |
| 104 | + </th> | |
| 105 | + <td> | |
| 106 | + <select | |
| 107 | + id="fileAtchPosblAt" | |
| 108 | + name="fileAtchPosblAt" | |
| 109 | + value={form.fileAtchPosblAt} | |
| 110 | + onChange={onChange} | |
| 111 | + > | |
| 112 | + <option value="Y">사용</option> | |
| 113 | + <option value="N">미사용</option> | |
| 114 | + </select> | |
| 115 | + </td> | |
| 116 | + </tr> | |
| 117 | + | |
| 118 | + <tr> | |
| 119 | + <th> | |
| 120 | + <span className="required">*</span> | |
| 121 | + 파일첨부갯수 | |
| 122 | + </th> | |
| 123 | + <td> | |
| 124 | + <select | |
| 125 | + id="posblAtchFileNumber" | |
| 126 | + name="posblAtchFileNumber" | |
| 127 | + value={form.posblAtchFileNumber} | |
| 128 | + onChange={onChange} | |
| 129 | + > | |
| 130 | + <option value="0">없음</option> | |
| 131 | + <option value="1">1</option> | |
| 132 | + <option value="9">9</option> | |
| 133 | + </select> | |
| 134 | + </td> | |
| 135 | + </tr> | |
| 136 | + | |
| 137 | + <tr> | |
| 138 | + <th> | |
| 139 | + <span className="required">*</span> | |
| 140 | + 파일첨부사이즈 | |
| 141 | + </th> | |
| 142 | + <td> | |
| 143 | + <input | |
| 144 | + type="number" | |
| 145 | + className="input" | |
| 146 | + id="posblAtchFileSize" | |
| 147 | + name="posblAtchFileSize" | |
| 148 | + value={form.posblAtchFileSize} | |
| 149 | + onChange={onChange} | |
| 150 | + /> | |
| 151 | + </td> | |
| 152 | + </tr> | |
| 153 | + | |
| 154 | + <tr> | |
| 155 | + <th> | |
| 156 | + <span className="required">*</span> | |
| 157 | + 조회수 노출여부 | |
| 158 | + </th> | |
| 159 | + <td> | |
| 160 | + <select | |
| 161 | + id="viewsYn" | |
| 162 | + name="viewsYn" | |
| 163 | + value={form.viewsYn} | |
| 164 | + onChange={onChange} | |
| 165 | + > | |
| 166 | + <option value="Y">사용</option> | |
| 167 | + <option value="N">미사용</option> | |
| 168 | + </select> | |
| 169 | + </td> | |
| 170 | + </tr> | |
| 171 | + | |
| 172 | + <tr> | |
| 173 | + <th> | |
| 174 | + <span className="required">*</span> | |
| 175 | + 게시판 사용여부 | |
| 176 | + </th> | |
| 177 | + <td> | |
| 178 | + <select | |
| 179 | + id="useAt" | |
| 180 | + name="useAt" | |
| 181 | + value={form.useAt} | |
| 182 | + onChange={onChange} | |
| 183 | + > | |
| 184 | + <option value="Y">사용</option> | |
| 185 | + <option value="N">미사용</option> | |
| 186 | + </select> | |
| 187 | + </td> | |
| 188 | + </tr> | |
| 189 | + </tbody> | |
| 190 | + </table> | |
| 191 | + </div> | |
| 192 | + ); | |
| 193 | +}; |
--- src/admin/feature/board/components/master/BoardListTable.tsx
+++ src/admin/feature/board/components/master/BoardListTable.tsx
... | ... | @@ -11,6 +11,11 @@ |
| 11 | 11 |
totalItems: number |
| 12 | 12 |
currentPage: number |
| 13 | 13 |
totalPages: number |
| 14 |
+ isAllChecked: boolean |
|
| 15 |
+ isPartiallyChecked: boolean |
|
| 16 |
+ isChecked: (id: string) => boolean |
|
| 17 |
+ onCheck: (id: string, checked: boolean) => void |
|
| 18 |
+ onCheckAll: (checked: boolean) => void |
|
| 14 | 19 |
onDetail: (bbsId: string) => void |
| 15 | 20 |
onArticleList: (bbsId: string) => void |
| 16 | 21 |
onPreview: (bbsId: string) => void |
... | ... | @@ -23,6 +28,11 @@ |
| 23 | 28 |
totalItems, |
| 24 | 29 |
currentPage, |
| 25 | 30 |
totalPages, |
| 31 |
+ isAllChecked, |
|
| 32 |
+ isPartiallyChecked, |
|
| 33 |
+ isChecked, |
|
| 34 |
+ onCheck, |
|
| 35 |
+ onCheckAll, |
|
| 26 | 36 |
onDetail, |
| 27 | 37 |
onArticleList, |
| 28 | 38 |
onPreview |
... | ... | @@ -31,7 +41,13 @@ |
| 31 | 41 |
return ( |
| 32 | 42 |
<div className="table table_type_cols"> |
| 33 | 43 |
<table> |
| 34 |
- <BoardListTableHeader params={params} onChange={onChange}/>
|
|
| 44 |
+ <BoardListTableHeader |
|
| 45 |
+ params={params}
|
|
| 46 |
+ onChange={onChange}
|
|
| 47 |
+ checked={isAllChecked}
|
|
| 48 |
+ indeterminate={isPartiallyChecked}
|
|
| 49 |
+ onCheckAll={onCheckAll}
|
|
| 50 |
+ /> |
|
| 35 | 51 |
<tbody> |
| 36 | 52 |
{items.length > 0 ?
|
| 37 | 53 |
items.map((item, index) => ( |
... | ... | @@ -43,6 +59,8 @@ |
| 43 | 59 |
totalItems={totalItems}
|
| 44 | 60 |
currentPage={currentPage}
|
| 45 | 61 |
totalPages={totalPages}
|
| 62 |
+ checked={isChecked(item.bbsId)}
|
|
| 63 |
+ onCheck={onCheck}
|
|
| 46 | 64 |
onDetail={onDetail}
|
| 47 | 65 |
onArticleList={onArticleList}
|
| 48 | 66 |
onPreview={onPreview}
|
--- src/admin/feature/board/components/master/BoardListTableHeader.tsx
+++ src/admin/feature/board/components/master/BoardListTableHeader.tsx
... | ... | @@ -1,11 +1,21 @@ |
| 1 | 1 |
import type {SearchParams} from "../../../../../type/searchParams.ts";
|
| 2 |
+import {CheckBox} from "../../../../component/checkbox/CheckBox.tsx";
|
|
| 2 | 3 |
|
| 3 | 4 |
interface BoardListTableHeaderProps {
|
| 4 | 5 |
params: SearchParams; |
| 5 | 6 |
onChange: (params: SearchParams) => void |
| 7 |
+ checked: boolean |
|
| 8 |
+ indeterminate: boolean |
|
| 9 |
+ onCheckAll: (checked: boolean) => void |
|
| 6 | 10 |
} |
| 7 | 11 |
|
| 8 |
-export function BoardListTableHeader({params, onChange}: BoardListTableHeaderProps) {
|
|
| 12 |
+export function BoardListTableHeader({
|
|
| 13 |
+ params, |
|
| 14 |
+ onChange, |
|
| 15 |
+ checked, |
|
| 16 |
+ indeterminate, |
|
| 17 |
+ onCheckAll |
|
| 18 |
+ }: BoardListTableHeaderProps) {
|
|
| 9 | 19 |
const handleSort = (field: string) => {
|
| 10 | 20 |
|
| 11 | 21 |
const nextOrder = |
... | ... | @@ -36,6 +46,7 @@ |
| 36 | 46 |
return ( |
| 37 | 47 |
<> |
| 38 | 48 |
<colgroup> |
| 49 |
+ <col style={{width: '40px'}}/>
|
|
| 39 | 50 |
<col style={{width: '6%'}}/>
|
| 40 | 51 |
<col style={{width: '18%'}}/>
|
| 41 | 52 |
<col style={{width: '18%'}}/>
|
... | ... | @@ -48,6 +59,15 @@ |
| 48 | 59 |
|
| 49 | 60 |
<thead> |
| 50 | 61 |
<tr> |
| 62 |
+ <th> |
|
| 63 |
+ <CheckBox |
|
| 64 |
+ id="boardCheckAll" |
|
| 65 |
+ name="checkAll" |
|
| 66 |
+ checked={checked}
|
|
| 67 |
+ indeterminate={indeterminate}
|
|
| 68 |
+ onChange={onCheckAll}
|
|
| 69 |
+ /> |
|
| 70 |
+ </th> |
|
| 51 | 71 |
<th>번호</th> |
| 52 | 72 |
<th> |
| 53 | 73 |
게시판명 |
--- src/admin/feature/board/components/master/BoardListTableRow.tsx
+++ src/admin/feature/board/components/master/BoardListTableRow.tsx
... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 |
import type {BoardListItem} from "../../type/board.types.ts";
|
| 2 | 2 |
import type {SearchParams} from "../../../../../type/searchParams.ts";
|
| 3 |
+import {CheckBox} from "../../../../component/checkbox/CheckBox.tsx";
|
|
| 3 | 4 |
|
| 4 | 5 |
interface BoardListTableRowProps {
|
| 5 | 6 |
item: BoardListItem |
... | ... | @@ -8,6 +9,8 @@ |
| 8 | 9 |
totalItems: number |
| 9 | 10 |
currentPage: number |
| 10 | 11 |
totalPages: number |
| 12 |
+ checked: boolean |
|
| 13 |
+ onCheck: (id: string, checked: boolean) => void |
|
| 11 | 14 |
onDetail: (bbsId: string) => void |
| 12 | 15 |
onArticleList: (bbsId: string) => void |
| 13 | 16 |
onPreview: (bbsId: string) => void |
... | ... | @@ -20,6 +23,8 @@ |
| 20 | 23 |
totalItems, |
| 21 | 24 |
currentPage, |
| 22 | 25 |
totalPages, |
| 26 |
+ checked, |
|
| 27 |
+ onCheck, |
|
| 23 | 28 |
onDetail, |
| 24 | 29 |
onArticleList, |
| 25 | 30 |
onPreview |
... | ... | @@ -31,6 +36,15 @@ |
| 31 | 36 |
|
| 32 | 37 |
return ( |
| 33 | 38 |
<tr> |
| 39 |
+ <td> |
|
| 40 |
+ <CheckBox |
|
| 41 |
+ id={`boardCheckList_${bbsId}`}
|
|
| 42 |
+ name="checkList" |
|
| 43 |
+ value={bbsId}
|
|
| 44 |
+ checked={checked}
|
|
| 45 |
+ onChange={(nextChecked) => onCheck(bbsId, nextChecked)}
|
|
| 46 |
+ /> |
|
| 47 |
+ </td> |
|
| 34 | 48 |
<td>{rowNumber}</td>
|
| 35 | 49 |
<td>{item.bbsNm}</td>
|
| 36 | 50 |
<td>{item.menuNm}</td>
|
+++ src/admin/feature/board/hook/mutation/useCreateBoard.ts
... | ... | @@ -0,0 +1,15 @@ |
| 1 | +import {useMutation, useQueryClient} from "@tanstack/react-query"; | |
| 2 | +import {createBoard} from "../../api/boardApi.ts"; | |
| 3 | + | |
| 4 | +export const useCreateBoard = () => { | |
| 5 | + const queryClient = useQueryClient(); | |
| 6 | + | |
| 7 | + return useMutation({ | |
| 8 | + mutationFn: createBoard, | |
| 9 | + onSuccess: () => { | |
| 10 | + queryClient.invalidateQueries({ | |
| 11 | + queryKey: ['boardList'] | |
| 12 | + }); | |
| 13 | + }, | |
| 14 | + }); | |
| 15 | +} |
+++ src/admin/feature/board/hook/mutation/useDeleteBoard.ts
... | ... | @@ -0,0 +1,15 @@ |
| 1 | +import {useMutation, useQueryClient} from "@tanstack/react-query"; | |
| 2 | +import {deleteBoard} from "../../api/boardApi.ts"; | |
| 3 | + | |
| 4 | +export const useDeleteBoard = () => { | |
| 5 | + const queryClient = useQueryClient(); | |
| 6 | + | |
| 7 | + return useMutation({ | |
| 8 | + mutationFn: deleteBoard, | |
| 9 | + onSuccess: () => { | |
| 10 | + queryClient.invalidateQueries({ | |
| 11 | + queryKey: ['boardList'] | |
| 12 | + }); | |
| 13 | + }, | |
| 14 | + }); | |
| 15 | +}; |
+++ src/admin/feature/board/hook/mutation/useUpdateBoard.ts
... | ... | @@ -0,0 +1,19 @@ |
| 1 | +import {useMutation, useQueryClient} from "@tanstack/react-query"; | |
| 2 | +import {updateBoard} from "../../api/boardApi.ts"; | |
| 3 | + | |
| 4 | +export const useUpdateBoard = () => { | |
| 5 | + const queryClient = useQueryClient(); | |
| 6 | + | |
| 7 | + return useMutation({ | |
| 8 | + mutationFn: updateBoard, | |
| 9 | + onSuccess: (_, variables) => { | |
| 10 | + queryClient.invalidateQueries({ | |
| 11 | + queryKey: ['boardList'] | |
| 12 | + }); | |
| 13 | + | |
| 14 | + queryClient.invalidateQueries({ | |
| 15 | + queryKey: ['boardDetail', variables.bbsId] | |
| 16 | + }); | |
| 17 | + }, | |
| 18 | + }); | |
| 19 | +}; |
+++ src/admin/feature/board/hook/page/useBoardArticleListPage.ts
... | ... | @@ -0,0 +1,105 @@ |
| 1 | +import {useMemo, useState} from "react"; | |
| 2 | +import {ADMIN_BBS_MASTER_ROUTE} from "../../../../route/adminRouteMap.ts"; | |
| 3 | +import {useCheckedList} from "../../../../hook/useCheckedList.ts"; | |
| 4 | +import type {BoardArticleSearchParams} from "../../type/board.types.ts"; | |
| 5 | +import {useBoardArticleList} from "../query/useBoardArticleList.ts"; | |
| 6 | + | |
| 7 | +const initSearchParam: BoardArticleSearchParams = { | |
| 8 | + pageIndex: 1, | |
| 9 | + pageUnit: 10, | |
| 10 | + searchCnd: "0", | |
| 11 | + searchKeyword: "", | |
| 12 | + searchSortCnd: "FRST_REGIST_PNTTM", | |
| 13 | + searchSortOrd: "ASC", | |
| 14 | + bbsId: "" | |
| 15 | +}; | |
| 16 | + | |
| 17 | +const searchOptions = [ | |
| 18 | + {value: '0', label: '제목'}, | |
| 19 | + {value: '1', label: '내용'}, | |
| 20 | + {value: '2', label: '작성자'}, | |
| 21 | +]; | |
| 22 | + | |
| 23 | +export const useBoardArticleListPage = (bbsId: string) => { | |
| 24 | + const [searchDraft, setSearchDraft] = useState<BoardArticleSearchParams>(initSearchParam); | |
| 25 | + const searchParams = useMemo( | |
| 26 | + () => ({ | |
| 27 | + ...searchDraft, | |
| 28 | + bbsId, | |
| 29 | + }), | |
| 30 | + [bbsId, searchDraft] | |
| 31 | + ); | |
| 32 | + | |
| 33 | + const { | |
| 34 | + list, | |
| 35 | + extraData, | |
| 36 | + totalItems, | |
| 37 | + currentPage, | |
| 38 | + totalPages, | |
| 39 | + size, | |
| 40 | + isLoading, | |
| 41 | + error | |
| 42 | + } = useBoardArticleList(searchParams); | |
| 43 | + const articleIds = useMemo( | |
| 44 | + () => list.map((item) => item.nttId), | |
| 45 | + [list] | |
| 46 | + ); | |
| 47 | + const { | |
| 48 | + checkedIds, | |
| 49 | + isAllChecked, | |
| 50 | + isPartiallyChecked, | |
| 51 | + isChecked, | |
| 52 | + handleCheck, | |
| 53 | + handleCheckAll, | |
| 54 | + resetChecked, | |
| 55 | + } = useCheckedList(articleIds); | |
| 56 | + const bbsNm = extraData?.boardMaster?.bbsNm ?? ''; | |
| 57 | + const bbsTyCode = extraData?.boardMaster?.bbsTyCode ?? ''; | |
| 58 | + const title = `${bbsNm || '게시글'} 목록`; | |
| 59 | + const breadcrumb = [ | |
| 60 | + {label: '게시판 관리', url: ADMIN_BBS_MASTER_ROUTE}, | |
| 61 | + {label: title} | |
| 62 | + ]; | |
| 63 | + const successMessage = bbsNm | |
| 64 | + ? `${bbsNm} 목록을 불러왔습니다.` | |
| 65 | + : '게시글 목록을 불러왔습니다.'; | |
| 66 | + | |
| 67 | + const handleSearchChange = (params: BoardArticleSearchParams) => { | |
| 68 | + setSearchDraft({ | |
| 69 | + ...params, | |
| 70 | + bbsId: "", | |
| 71 | + }); | |
| 72 | + }; | |
| 73 | + | |
| 74 | + const handlePageChange = (pageIndex: number) => { | |
| 75 | + setSearchDraft((prev) => ({ | |
| 76 | + ...prev, | |
| 77 | + pageIndex, | |
| 78 | + })); | |
| 79 | + }; | |
| 80 | + | |
| 81 | + return { | |
| 82 | + title, | |
| 83 | + breadcrumb, | |
| 84 | + searchOptions, | |
| 85 | + searchParams, | |
| 86 | + list, | |
| 87 | + bbsTyCode, | |
| 88 | + totalItems, | |
| 89 | + currentPage, | |
| 90 | + totalPages, | |
| 91 | + size, | |
| 92 | + isLoading, | |
| 93 | + error, | |
| 94 | + successMessage, | |
| 95 | + checkedIds, | |
| 96 | + isAllChecked, | |
| 97 | + isPartiallyChecked, | |
| 98 | + isChecked, | |
| 99 | + handleCheck, | |
| 100 | + handleCheckAll, | |
| 101 | + resetChecked, | |
| 102 | + handleSearchChange, | |
| 103 | + handlePageChange, | |
| 104 | + }; | |
| 105 | +}; |
+++ src/admin/feature/board/hook/page/useBoardForm.ts
... | ... | @@ -0,0 +1,154 @@ |
| 1 | +import {type ChangeEvent, useMemo, useState} from "react"; | |
| 2 | +import {useNavigate} from "react-router-dom"; | |
| 3 | +import {toast} from "react-toastify"; | |
| 4 | +import {ADMIN_BBS_MASTER_ROUTE} from "../../../../route/adminRouteMap.ts"; | |
| 5 | +import type {CommonCodeItem} from "../../../../../type/code.ts"; | |
| 6 | +import type {BoardFormItem} from "../../type/board.types.ts"; | |
| 7 | +import {useBoardDetail} from "../query/useBoardDetail.ts"; | |
| 8 | +import {useCreateBoard} from "../mutation/useCreateBoard.ts"; | |
| 9 | +import {useDeleteBoard} from "../mutation/useDeleteBoard.ts"; | |
| 10 | +import {useUpdateBoard} from "../mutation/useUpdateBoard.ts"; | |
| 11 | + | |
| 12 | +export type BoardFormMode = 'create' | 'update'; | |
| 13 | + | |
| 14 | +const initBoardFormData: BoardFormItem = { | |
| 15 | + bbsId: '', | |
| 16 | + bbsNm: '', | |
| 17 | + bbsTyCode: '', | |
| 18 | + noticeYn: 'Y', | |
| 19 | + addYn: 'Y', | |
| 20 | + fileAtchPosblAt: 'Y', | |
| 21 | + posblAtchFileNumber: '0', | |
| 22 | + posblAtchFileSize: '0', | |
| 23 | + viewsYn: 'Y', | |
| 24 | + useAt: 'Y', | |
| 25 | +}; | |
| 26 | + | |
| 27 | +const createInitialForm = ( | |
| 28 | + item?: BoardFormItem, | |
| 29 | + typeList: CommonCodeItem[] = [] | |
| 30 | +) => ({ | |
| 31 | + ...initBoardFormData, | |
| 32 | + bbsTyCode: typeList[0]?.code ?? '', | |
| 33 | + ...item, | |
| 34 | +}); | |
| 35 | + | |
| 36 | +export const useBoardForm = (bbsId: string) => { | |
| 37 | + const navigate = useNavigate(); | |
| 38 | + const mode: BoardFormMode = bbsId ? 'update' : 'create'; | |
| 39 | + const [formDraft, setFormDraft] = useState<Partial<BoardFormItem>>({}); | |
| 40 | + | |
| 41 | + const {data, isLoading, error} = useBoardDetail(bbsId, {enabled: !!bbsId}); | |
| 42 | + const {mutateAsync: createBoard, isPending: isCreating} = useCreateBoard(); | |
| 43 | + const {mutateAsync: updateBoard, isPending: isUpdating} = useUpdateBoard(); | |
| 44 | + const {mutateAsync: deleteBoard, isPending: isDeleting} = useDeleteBoard(); | |
| 45 | + const isPending = isCreating || isUpdating || isDeleting; | |
| 46 | + | |
| 47 | + const title = `게시판 ${mode === 'create' ? '생성' : '수정'}`; | |
| 48 | + const breadcrumb = [ | |
| 49 | + {label: '게시판 관리', url: ADMIN_BBS_MASTER_ROUTE}, | |
| 50 | + {label: title} | |
| 51 | + ]; | |
| 52 | + const baseForm = useMemo( | |
| 53 | + () => createInitialForm(data?.result, data?.typeList), | |
| 54 | + [data?.result, data?.typeList] | |
| 55 | + ); | |
| 56 | + const form = { | |
| 57 | + ...baseForm, | |
| 58 | + ...formDraft, | |
| 59 | + }; | |
| 60 | + | |
| 61 | + const handleChange = (event: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => { | |
| 62 | + const {name, value} = event.target; | |
| 63 | + | |
| 64 | + setFormDraft((prev) => ({ | |
| 65 | + ...prev, | |
| 66 | + [name]: value, | |
| 67 | + })); | |
| 68 | + }; | |
| 69 | + | |
| 70 | + const validateForm = () => { | |
| 71 | + if (!form.bbsNm.trim()) { | |
| 72 | + toast.warning('게시판명을 입력해주세요.'); | |
| 73 | + return false; | |
| 74 | + } | |
| 75 | + | |
| 76 | + if (!form.bbsTyCode) { | |
| 77 | + toast.warning('게시판유형을 선택해주세요.'); | |
| 78 | + return false; | |
| 79 | + } | |
| 80 | + | |
| 81 | + return true; | |
| 82 | + }; | |
| 83 | + | |
| 84 | + const handleCreate = async () => { | |
| 85 | + if (!validateForm()) { | |
| 86 | + return; | |
| 87 | + } | |
| 88 | + | |
| 89 | + await toast.promise( | |
| 90 | + createBoard(form), | |
| 91 | + { | |
| 92 | + pending: '등록 중...', | |
| 93 | + success: '등록 완료', | |
| 94 | + error: '등록 실패' | |
| 95 | + } | |
| 96 | + ); | |
| 97 | + | |
| 98 | + navigate(ADMIN_BBS_MASTER_ROUTE); | |
| 99 | + }; | |
| 100 | + | |
| 101 | + const handleUpdate = async () => { | |
| 102 | + if (!validateForm()) { | |
| 103 | + return; | |
| 104 | + } | |
| 105 | + | |
| 106 | + await toast.promise( | |
| 107 | + updateBoard(form), | |
| 108 | + { | |
| 109 | + pending: '수정 중...', | |
| 110 | + success: '수정 완료', | |
| 111 | + error: '수정 실패' | |
| 112 | + } | |
| 113 | + ); | |
| 114 | + | |
| 115 | + navigate(ADMIN_BBS_MASTER_ROUTE); | |
| 116 | + }; | |
| 117 | + | |
| 118 | + const handleDelete = async () => { | |
| 119 | + if (!bbsId || !window.confirm('게시판을 삭제하시겠습니까?')) { | |
| 120 | + return; | |
| 121 | + } | |
| 122 | + | |
| 123 | + await toast.promise( | |
| 124 | + deleteBoard(bbsId), | |
| 125 | + { | |
| 126 | + pending: '삭제 중...', | |
| 127 | + success: '삭제 완료', | |
| 128 | + error: '삭제 실패' | |
| 129 | + } | |
| 130 | + ); | |
| 131 | + | |
| 132 | + navigate(ADMIN_BBS_MASTER_ROUTE); | |
| 133 | + }; | |
| 134 | + | |
| 135 | + const handleList = () => { | |
| 136 | + navigate(ADMIN_BBS_MASTER_ROUTE); | |
| 137 | + }; | |
| 138 | + | |
| 139 | + return { | |
| 140 | + mode, | |
| 141 | + title, | |
| 142 | + breadcrumb, | |
| 143 | + form, | |
| 144 | + typeList: data?.typeList, | |
| 145 | + isLoading, | |
| 146 | + error, | |
| 147 | + isPending, | |
| 148 | + handleChange, | |
| 149 | + handleCreate, | |
| 150 | + handleUpdate, | |
| 151 | + handleDelete, | |
| 152 | + handleList, | |
| 153 | + }; | |
| 154 | +}; |
+++ src/admin/feature/board/hook/page/useBoardListPage.ts
... | ... | @@ -0,0 +1,117 @@ |
| 1 | +import {useMemo, useState} from "react"; | |
| 2 | +import type {BoardSearchParams} from "../../type/board.types.ts"; | |
| 3 | +import {useBoardList} from "../query/useBoardList.ts"; | |
| 4 | +import {ADMIN_BBS_ARTICLE_DETAIL_ROUTE, ADMIN_BBS_ARTICLE_LIST_ROUTE} from "../../../../route/adminRouteMap.ts"; | |
| 5 | +import {useNavigate} from "react-router-dom"; | |
| 6 | +import {useCheckedList} from "../../../../hook/useCheckedList.ts"; | |
| 7 | +import {toast} from "react-toastify"; | |
| 8 | + | |
| 9 | +const initSearchParam: BoardSearchParams = { | |
| 10 | + pageIndex: 1, | |
| 11 | + pageUnit: 10, | |
| 12 | + searchCnd: "0", | |
| 13 | + searchKeyword: "", | |
| 14 | + searchSortCnd: "BBS_NM", | |
| 15 | + searchSortOrd: "ASC" | |
| 16 | +} | |
| 17 | + | |
| 18 | +const searchOptions = [ | |
| 19 | + {value: '0', label: '게시판명/연결메뉴'}, | |
| 20 | + {value: '1', label: '게시판유형'}, | |
| 21 | +] | |
| 22 | + | |
| 23 | +const pageSizeOptions = [ | |
| 24 | + {value: '10', label: '10건씩'}, | |
| 25 | + {value: '20', label: '20건씩'}, | |
| 26 | + {value: '30', label: '30건씩'}, | |
| 27 | +] | |
| 28 | + | |
| 29 | +export const useBoardListPage = () => { | |
| 30 | + | |
| 31 | + const [searchParams, setSearchParams] = useState(initSearchParam); | |
| 32 | + const { | |
| 33 | + list, | |
| 34 | + totalItems, | |
| 35 | + totalPages, | |
| 36 | + currentPage, | |
| 37 | + size, | |
| 38 | + isLoading, | |
| 39 | + error | |
| 40 | + } = useBoardList(searchParams); | |
| 41 | + const boardIds = useMemo( | |
| 42 | + () => list.map((item) => item.bbsId), | |
| 43 | + [list] | |
| 44 | + ); | |
| 45 | + const { | |
| 46 | + checkedIds, | |
| 47 | + isAllChecked, | |
| 48 | + isPartiallyChecked, | |
| 49 | + isChecked, | |
| 50 | + handleCheck, | |
| 51 | + handleCheckAll, | |
| 52 | + resetChecked, | |
| 53 | + } = useCheckedList(boardIds); | |
| 54 | + const title = '게시판 관리'; | |
| 55 | + const breadcrumb = [{label: '게시판 관리'}]; | |
| 56 | + const homeUrl = '#'; | |
| 57 | + const successMessage = "게시판을 조회하였습니다."; | |
| 58 | + const navigate = useNavigate(); | |
| 59 | + | |
| 60 | + const handleDetail = (bbsId: string) => { | |
| 61 | + navigate(ADMIN_BBS_ARTICLE_DETAIL_ROUTE + bbsId); | |
| 62 | + } | |
| 63 | + const handleArticleList = (bbsId: string) => { | |
| 64 | + navigate(ADMIN_BBS_ARTICLE_LIST_ROUTE + bbsId); | |
| 65 | + } | |
| 66 | + const handlePreview = (bbsId: string) => { | |
| 67 | + navigate(`/preview/${bbsId}`); | |
| 68 | + } | |
| 69 | + const handlePageChange = (pageIndex: number) => { | |
| 70 | + setSearchParams((prev) => ({ | |
| 71 | + ...prev, | |
| 72 | + pageIndex, | |
| 73 | + })); | |
| 74 | + } | |
| 75 | + const handleDeleteBatch = () => { | |
| 76 | + if (checkedIds.length === 0) { | |
| 77 | + toast.warning('미사용 처리할 게시판을 선택해주세요.'); | |
| 78 | + return; | |
| 79 | + } else { | |
| 80 | + toast.info("삭제로직은 아직 미처리"); | |
| 81 | + } | |
| 82 | + } | |
| 83 | + const handleCreate = () => { | |
| 84 | + navigate(ADMIN_BBS_ARTICLE_DETAIL_ROUTE); | |
| 85 | + } | |
| 86 | + | |
| 87 | + return { | |
| 88 | + list, | |
| 89 | + totalItems, | |
| 90 | + totalPages, | |
| 91 | + currentPage, | |
| 92 | + size, | |
| 93 | + isLoading, | |
| 94 | + error, | |
| 95 | + title, | |
| 96 | + breadcrumb, | |
| 97 | + homeUrl, | |
| 98 | + searchOptions, | |
| 99 | + pageSizeOptions, | |
| 100 | + successMessage, | |
| 101 | + searchParams, | |
| 102 | + setSearchParams, | |
| 103 | + handleDetail, | |
| 104 | + handleArticleList, | |
| 105 | + handlePreview, | |
| 106 | + handlePageChange, | |
| 107 | + handleDeleteBatch, | |
| 108 | + handleCreate, | |
| 109 | + checkedIds, | |
| 110 | + isAllChecked, | |
| 111 | + isPartiallyChecked, | |
| 112 | + isChecked, | |
| 113 | + handleCheck, | |
| 114 | + handleCheckAll, | |
| 115 | + resetChecked, | |
| 116 | + } | |
| 117 | +} |
--- src/admin/feature/board/hook/useBoardArticleList.ts
+++ src/admin/feature/board/hook/query/useBoardArticleList.ts
... | ... | @@ -1,14 +1,13 @@ |
| 1 | 1 |
import {keepPreviousData, useQuery} from "@tanstack/react-query";
|
| 2 |
-import {fetchBoardArticleList} from "../api/boardApi.ts";
|
|
| 3 |
-import type {BoardArticleSearchParams} from "../type/board.types.ts";
|
|
| 2 |
+import {fetchBoardArticleList} from "../../api/boardApi.ts";
|
|
| 3 |
+import type {BoardArticleSearchParams} from "../../type/board.types.ts";
|
|
| 4 | 4 |
|
| 5 | 5 |
export function useBoardArticleList(searchParams: BoardArticleSearchParams) {
|
| 6 | 6 |
const query = useQuery({
|
| 7 |
- queryKey: ['boardArticleList'], |
|
| 7 |
+ queryKey: ['boardArticleList', searchParams], |
|
| 8 | 8 |
queryFn: () => fetchBoardArticleList(searchParams), |
| 9 | 9 |
placeholderData: keepPreviousData, |
| 10 | 10 |
}); |
| 11 |
- console.log("useBoardArticleList", query);
|
|
| 12 | 11 |
|
| 13 | 12 |
return {
|
| 14 | 13 |
list : query.data?.list ?? [], |
... | ... | @@ -20,4 +19,4 @@ |
| 20 | 19 |
isLoading: query.isLoading, |
| 21 | 20 |
error: query.error, |
| 22 | 21 |
} |
| 23 |
-}(No newline at end of file) |
|
| 22 |
+} |
+++ src/admin/feature/board/hook/query/useBoardDetail.ts
... | ... | @@ -0,0 +1,19 @@ |
| 1 | +import {fetchBoardDetail} from "../../api/boardApi.ts"; | |
| 2 | +import {keepPreviousData, useQuery} from "@tanstack/react-query"; | |
| 3 | + | |
| 4 | +type UseBoardDetailOptions = { | |
| 5 | + enabled: boolean; | |
| 6 | +} | |
| 7 | + | |
| 8 | +export const useBoardDetail = ( | |
| 9 | + bbsId: string, | |
| 10 | + options?: UseBoardDetailOptions | |
| 11 | +) => { | |
| 12 | + | |
| 13 | + return useQuery({ | |
| 14 | + queryKey: ['boardDetail', bbsId], | |
| 15 | + queryFn: () => fetchBoardDetail(bbsId), | |
| 16 | + placeholderData: keepPreviousData, | |
| 17 | + enabled: options?.enabled ?? true | |
| 18 | + }); | |
| 19 | +} |
--- src/admin/feature/board/hook/useBoardList.ts
+++ src/admin/feature/board/hook/query/useBoardList.ts
... | ... | @@ -1,8 +1,8 @@ |
| 1 | 1 |
import {keepPreviousData, useQuery} from "@tanstack/react-query";
|
| 2 |
-import {fetchBoardList} from "../api/boardApi.ts";
|
|
| 3 |
-import type {SearchParams} from "../../../../type/searchParams.ts";
|
|
| 2 |
+import {fetchBoardList} from "../../api/boardApi.ts";
|
|
| 3 |
+import type {SearchParams} from "../../../../../type/searchParams.ts";
|
|
| 4 | 4 |
|
| 5 |
-export function useBoardListQuery(searchParams: SearchParams) {
|
|
| 5 |
+export function useBoardList(searchParams: SearchParams) {
|
|
| 6 | 6 |
|
| 7 | 7 |
const query = useQuery({
|
| 8 | 8 |
queryKey: ['boardList', searchParams], |
... | ... | @@ -13,8 +13,9 @@ |
| 13 | 13 |
return {
|
| 14 | 14 |
list: query.data?.list ?? [], |
| 15 | 15 |
totalItems: query.data?.totalItems ?? 0, |
| 16 |
- currentPage: query.data?.currentPage ?? 0, |
|
| 17 | 16 |
totalPages: query.data?.totalPages ?? 0, |
| 17 |
+ currentPage: query.data?.currentPage ?? 0, |
|
| 18 |
+ size: query.data?.size ?? 0, |
|
| 18 | 19 |
isLoading: query.isLoading, |
| 19 | 20 |
error: query.error, |
| 20 | 21 |
} |
--- src/admin/feature/board/page/BoardArticleListPage.tsx
+++ src/admin/feature/board/page/BoardArticleListPage.tsx
... | ... | @@ -1,57 +1,43 @@ |
| 1 | 1 |
import {useParams} from "react-router-dom";
|
| 2 |
-import {useBoardArticleList} from "../hook/useBoardArticleList.ts";
|
|
| 3 |
-import type {BoardArticleSearchParams} from "../type/board.types.ts";
|
|
| 4 |
-import {useState} from "react";
|
|
| 5 | 2 |
import {PageHeader} from "../../../component/PageHeader.tsx";
|
| 6 | 3 |
import {ListSearchForm} from "../../../component/ListSearchForm.tsx";
|
| 7 | 4 |
import {Pagination} from "../../../component/pagination/Pagination.tsx";
|
| 8 | 5 |
import {useLoadingToast} from "../../../hook/useLoadingToast.ts";
|
| 9 | 6 |
import {BoartArticleListTable} from "../components/article/BoardArticleListTable.tsx";
|
| 10 | 7 |
import {BoardArticleImageListTable} from "../components/article/BoardArticleImageListTable.tsx";
|
| 11 |
- |
|
| 12 |
-const initSearchParam: BoardArticleSearchParams = {
|
|
| 13 |
- pageIndex: 1, |
|
| 14 |
- pageUnit: 10, |
|
| 15 |
- searchCnd: "0", |
|
| 16 |
- searchKeyword: "", |
|
| 17 |
- searchSortCnd: "FRST_REGIST_PNTTM", |
|
| 18 |
- searchSortOrd: "ASC", |
|
| 19 |
- bbsId: "" |
|
| 20 |
-}; |
|
| 8 |
+import {useBoardArticleListPage} from "../hook/page/useBoardArticleListPage.ts";
|
|
| 21 | 9 |
|
| 22 | 10 |
export const BoardArticleListPage = () => {
|
| 23 | 11 |
const {bbsId = ''} = useParams();
|
| 24 |
- const searchOptions = [ |
|
| 25 |
- {value: '0', label: '제목'},
|
|
| 26 |
- {value: '1', label: '내용'},
|
|
| 27 |
- {value: '2', label: '작성자'},
|
|
| 28 |
- ] |
|
| 29 |
- |
|
| 30 |
- const [searchParams, setSearchParams] = useState<BoardArticleSearchParams>({
|
|
| 31 |
- ...initSearchParam, |
|
| 32 |
- bbsId, |
|
| 33 |
- }); |
|
| 34 | 12 |
const {
|
| 13 |
+ title, |
|
| 14 |
+ breadcrumb, |
|
| 15 |
+ searchOptions, |
|
| 16 |
+ searchParams, |
|
| 35 | 17 |
list, |
| 36 |
- extraData, |
|
| 18 |
+ bbsTyCode, |
|
| 37 | 19 |
totalItems, |
| 38 | 20 |
currentPage, |
| 39 | 21 |
totalPages, |
| 40 | 22 |
size, |
| 41 | 23 |
isLoading, |
| 42 |
- error |
|
| 43 |
- } = useBoardArticleList(searchParams); |
|
| 24 |
+ error, |
|
| 25 |
+ successMessage, |
|
| 26 |
+ isAllChecked, |
|
| 27 |
+ isPartiallyChecked, |
|
| 28 |
+ isChecked, |
|
| 29 |
+ handleCheck, |
|
| 30 |
+ handleCheckAll, |
|
| 31 |
+ handleSearchChange, |
|
| 32 |
+ handlePageChange, |
|
| 33 |
+ } = useBoardArticleListPage(bbsId); |
|
| 44 | 34 |
|
| 45 |
- const bbsNm = extraData ? extraData.boardMaster?.bbsNm : null; |
|
| 46 |
- const bbsTyCode = extraData ? extraData.boardMaster?.bbsTyCode : null; |
|
| 47 | 35 |
useLoadingToast({
|
| 48 | 36 |
isLoading, |
| 49 | 37 |
error, |
| 50 |
- successMessage: `${bbsNm} 목록을 불러왔습니다.`
|
|
| 38 |
+ successMessage |
|
| 51 | 39 |
}); |
| 52 | 40 |
|
| 53 |
- const title = `${bbsNm} 목록`
|
|
| 54 |
- const breadcrumb = [{label: '게시판 관리', url: '/admin/cop/bbs/SelectBBSMasterInfs.do'}, {label: `${bbsNm} 목록`}]
|
|
| 55 | 41 |
const homeUrl = '#' |
| 56 | 42 |
|
| 57 | 43 |
return ( |
... | ... | @@ -60,7 +46,7 @@ |
| 60 | 46 |
<ListSearchForm |
| 61 | 47 |
totalItems={totalItems}
|
| 62 | 48 |
searchParams={searchParams}
|
| 63 |
- onChange={setSearchParams}
|
|
| 49 |
+ onChange={handleSearchChange}
|
|
| 64 | 50 |
searchOptions={searchOptions}
|
| 65 | 51 |
totalLabel={"게시글"}
|
| 66 | 52 |
/> |
... | ... | @@ -71,7 +57,12 @@ |
| 71 | 57 |
<BoartArticleListTable |
| 72 | 58 |
items={list}
|
| 73 | 59 |
params={searchParams}
|
| 74 |
- onChange={setSearchParams}
|
|
| 60 |
+ onChange={handleSearchChange}
|
|
| 61 |
+ isAllChecked={isAllChecked}
|
|
| 62 |
+ isPartiallyChecked={isPartiallyChecked}
|
|
| 63 |
+ isChecked={isChecked}
|
|
| 64 |
+ onCheck={handleCheck}
|
|
| 65 |
+ onCheckAll={handleCheckAll}
|
|
| 75 | 66 |
totalPages={totalPages}
|
| 76 | 67 |
currentPage={currentPage}
|
| 77 | 68 |
totalItems={totalItems}
|
... | ... | @@ -82,9 +73,8 @@ |
| 82 | 73 |
totalPages={totalPages}
|
| 83 | 74 |
currentPage={currentPage}
|
| 84 | 75 |
size={size}
|
| 85 |
- onPageChange={() => {
|
|
| 86 |
- }} |
|
| 76 |
+ onPageChange={handlePageChange}
|
|
| 87 | 77 |
/> |
| 88 | 78 |
</> |
| 89 | 79 |
); |
| 90 |
-}(No newline at end of file) |
|
| 80 |
+} |
+++ src/admin/feature/board/page/BoardFormPage.tsx
... | ... | @@ -0,0 +1,50 @@ |
| 1 | +import {useParams} from "react-router-dom"; | |
| 2 | +import {PageHeader} from "../../../component/PageHeader.tsx"; | |
| 3 | +import {useLoadingToast} from "../../../hook/useLoadingToast.ts"; | |
| 4 | +import {ActionButtonFormGroup} from "../../../component/button/ActionButtonFormGroup.tsx"; | |
| 5 | +import {BoardFormTable} from "../components/master/BoardFormTable.tsx"; | |
| 6 | +import {useBoardForm} from "../hook/page/useBoardForm.ts"; | |
| 7 | + | |
| 8 | +export const BoardFormPage = () => { | |
| 9 | + const {bbsId = ''} = useParams(); | |
| 10 | + const { | |
| 11 | + mode, | |
| 12 | + title, | |
| 13 | + breadcrumb, | |
| 14 | + form, | |
| 15 | + typeList, | |
| 16 | + isLoading, | |
| 17 | + error, | |
| 18 | + isPending, | |
| 19 | + handleChange, | |
| 20 | + handleCreate, | |
| 21 | + handleUpdate, | |
| 22 | + handleDelete, | |
| 23 | + handleList, | |
| 24 | + } = useBoardForm(bbsId); | |
| 25 | + | |
| 26 | + useLoadingToast({ | |
| 27 | + isLoading, | |
| 28 | + error, | |
| 29 | + successMessage: '데이터 조회가 완료되었습니다.' | |
| 30 | + }); | |
| 31 | + | |
| 32 | + return ( | |
| 33 | + <> | |
| 34 | + <PageHeader title={title} breadcrumb={breadcrumb} homeUrl="#"/> | |
| 35 | + <BoardFormTable | |
| 36 | + form={form} | |
| 37 | + typeList={typeList} | |
| 38 | + onChange={handleChange} | |
| 39 | + /> | |
| 40 | + <ActionButtonFormGroup | |
| 41 | + mode={mode} | |
| 42 | + disabled={isPending} | |
| 43 | + onDelete={handleDelete} | |
| 44 | + onCreate={handleCreate} | |
| 45 | + onUpdate={handleUpdate} | |
| 46 | + onList={handleList} | |
| 47 | + /> | |
| 48 | + </> | |
| 49 | + ); | |
| 50 | +}; |
--- src/admin/feature/board/page/BoardListPage.tsx
+++ src/admin/feature/board/page/BoardListPage.tsx
... | ... | @@ -1,62 +1,46 @@ |
| 1 |
-import {useBoardListQuery} from "../hook/useBoardList.ts";
|
|
| 2 | 1 |
import {PageHeader} from "../../../component/PageHeader.tsx";
|
| 3 |
-import {useState} from "react";
|
|
| 4 | 2 |
import {ListSearchForm} from "../../../component/ListSearchForm.tsx";
|
| 5 | 3 |
import {BoardListTable} from "../components/master/BoardListTable.tsx";
|
| 6 |
-import {useNavigate} from "react-router-dom";
|
|
| 7 |
-import type {BoardSearchParams} from "../type/board.types.ts";
|
|
| 8 | 4 |
import {useLoadingToast} from "../../../hook/useLoadingToast.ts";
|
| 9 |
- |
|
| 10 |
-const initSearchParam: BoardSearchParams = {
|
|
| 11 |
- pageIndex: 1, |
|
| 12 |
- pageUnit: 10, |
|
| 13 |
- searchCnd: "0", |
|
| 14 |
- searchKeyword: "", |
|
| 15 |
- searchSortCnd: "BBS_NM", |
|
| 16 |
- searchSortOrd: "ASC" |
|
| 17 |
-} |
|
| 5 |
+import {Pagination} from "../../../component/pagination/Pagination.tsx";
|
|
| 6 |
+import {ActionButtonListGroup} from "../../../component/button/ActionButtonListGroup.tsx";
|
|
| 7 |
+import {useBoardListPage} from "../hook/page/useBoardListPage.ts";
|
|
| 18 | 8 |
|
| 19 | 9 |
export const BoardListPage = () => {
|
| 20 |
- const searchOptions = [ |
|
| 21 |
- {value: '0', label: '게시판명/연결메뉴'},
|
|
| 22 |
- {value: '1', label: '게시판유형'},
|
|
| 23 |
- ] |
|
| 24 |
- |
|
| 25 |
- const pageSizeOptions = [ |
|
| 26 |
- {value: '10', label: '10건씩'},
|
|
| 27 |
- {value: '20', label: '20건씩'},
|
|
| 28 |
- {value: '30', label: '30건씩'},
|
|
| 29 |
- ] |
|
| 30 |
- const [searchParams, setSearchParams] = useState<BoardSearchParams>(initSearchParam); |
|
| 31 | 10 |
const {
|
| 32 | 11 |
list, |
| 33 | 12 |
totalItems, |
| 34 |
- currentPage, |
|
| 35 | 13 |
totalPages, |
| 14 |
+ currentPage, |
|
| 15 |
+ size, |
|
| 36 | 16 |
isLoading, |
| 37 |
- error |
|
| 38 |
- } = useBoardListQuery(searchParams); |
|
| 39 |
- const navigate = useNavigate(); |
|
| 40 |
- |
|
| 41 |
- const handleDetail = (bbsId: string) => {
|
|
| 42 |
- navigate(`/admin/cop/bbs/detail/${bbsId}`);
|
|
| 43 |
- } |
|
| 44 |
- const handleArticleList = (bbsId: string) => {
|
|
| 45 |
- navigate(`/detail/cop/bbs/article/${bbsId}`);
|
|
| 46 |
- } |
|
| 47 |
- const handlePreview = (bbsId: string) => {
|
|
| 48 |
- navigate(`/preview/${bbsId}`);
|
|
| 49 |
- } |
|
| 17 |
+ error, |
|
| 18 |
+ title, |
|
| 19 |
+ breadcrumb, |
|
| 20 |
+ homeUrl, |
|
| 21 |
+ searchOptions, |
|
| 22 |
+ pageSizeOptions, |
|
| 23 |
+ successMessage, |
|
| 24 |
+ searchParams, |
|
| 25 |
+ setSearchParams, |
|
| 26 |
+ handleDetail, |
|
| 27 |
+ handleArticleList, |
|
| 28 |
+ handlePreview, |
|
| 29 |
+ handlePageChange, |
|
| 30 |
+ handleDeleteBatch, |
|
| 31 |
+ handleCreate, |
|
| 32 |
+ isAllChecked, |
|
| 33 |
+ isPartiallyChecked, |
|
| 34 |
+ isChecked, |
|
| 35 |
+ handleCheck, |
|
| 36 |
+ handleCheckAll, |
|
| 37 |
+ } = useBoardListPage(); |
|
| 50 | 38 |
|
| 51 | 39 |
useLoadingToast({
|
| 52 | 40 |
isLoading, |
| 53 | 41 |
error, |
| 54 |
- successMessage : '게시판 마스터 목록을 조회하였습니다.' |
|
| 42 |
+ successMessage |
|
| 55 | 43 |
}); |
| 56 |
- |
|
| 57 |
- const title = '게시판 관리' |
|
| 58 |
- const breadcrumb = [{label: '게시판 관리'}]
|
|
| 59 |
- const homeUrl = '#' |
|
| 60 | 44 |
|
| 61 | 45 |
return ( |
| 62 | 46 |
<> |
... | ... | @@ -82,10 +66,27 @@ |
| 82 | 66 |
totalItems={totalItems}
|
| 83 | 67 |
currentPage={currentPage}
|
| 84 | 68 |
totalPages={totalPages}
|
| 69 |
+ isAllChecked={isAllChecked}
|
|
| 70 |
+ isPartiallyChecked={isPartiallyChecked}
|
|
| 71 |
+ isChecked={isChecked}
|
|
| 72 |
+ onCheck={handleCheck}
|
|
| 73 |
+ onCheckAll={handleCheckAll}
|
|
| 85 | 74 |
onDetail={handleDetail}
|
| 86 | 75 |
onArticleList={handleArticleList}
|
| 87 | 76 |
onPreview={handlePreview}
|
| 88 | 77 |
/> |
| 78 |
+ <ActionButtonListGroup |
|
| 79 |
+ onDelete={handleDeleteBatch}
|
|
| 80 |
+ onCreate={handleCreate}
|
|
| 81 |
+ deleteLabel={"미사용"}
|
|
| 82 |
+ /> |
|
| 83 |
+ <Pagination |
|
| 84 |
+ totalItems={totalItems}
|
|
| 85 |
+ totalPages={totalPages}
|
|
| 86 |
+ currentPage={currentPage}
|
|
| 87 |
+ size={size}
|
|
| 88 |
+ onPageChange={handlePageChange}
|
|
| 89 |
+ /> |
|
| 89 | 90 |
</> |
| 90 | 91 |
) |
| 91 | 92 |
}; |
--- src/admin/feature/board/type/board.types.ts
+++ src/admin/feature/board/type/board.types.ts
... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 |
import type {SearchParams} from "../../../../type/searchParams.ts";
|
| 2 |
+import type {CommonCodeItem} from "../../../../type/code.ts";
|
|
| 2 | 3 |
|
| 3 | 4 |
export interface BoardSearchParams extends SearchParams {
|
| 4 | 5 |
|
... | ... | @@ -37,4 +38,22 @@ |
| 37 | 38 |
bbsNm: string; |
| 38 | 39 |
bbsTyCode: string; |
| 39 | 40 |
} |
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+export interface BoardFormItem {
|
|
| 44 |
+ bbsId: string; |
|
| 45 |
+ bbsNm: string; |
|
| 46 |
+ bbsTyCode: string; |
|
| 47 |
+ noticeYn: string; |
|
| 48 |
+ addYn: string; |
|
| 49 |
+ fileAtchPosblAt: string; |
|
| 50 |
+ posblAtchFileNumber: string; |
|
| 51 |
+ posblAtchFileSize: string; |
|
| 52 |
+ viewsYn: string; |
|
| 53 |
+ useAt: string; |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+export interface BoardDetailResponse {
|
|
| 57 |
+ result: BoardFormItem; |
|
| 58 |
+ typeList: CommonCodeItem[]; |
|
| 40 | 59 |
}(No newline at end of file) |
+++ src/admin/hook/useCheckedList.ts
... | ... | @@ -0,0 +1,44 @@ |
| 1 | +import {useMemo, useState} from "react"; | |
| 2 | + | |
| 3 | +type CheckedListId = string | number; | |
| 4 | + | |
| 5 | +export const useCheckedList = <T extends CheckedListId>(ids: T[]) => { | |
| 6 | + const [checkedIds, setCheckedIds] = useState<T[]>([]); | |
| 7 | + | |
| 8 | + const currentCheckedIds = useMemo( | |
| 9 | + () => checkedIds.filter((checkedId) => ids.includes(checkedId)), | |
| 10 | + [checkedIds, ids] | |
| 11 | + ); | |
| 12 | + const isAllChecked = ids.length > 0 && ids.every((id) => checkedIds.includes(id)); | |
| 13 | + const isPartiallyChecked = currentCheckedIds.length > 0 && !isAllChecked; | |
| 14 | + | |
| 15 | + const isChecked = (id: T) => checkedIds.includes(id); | |
| 16 | + | |
| 17 | + const handleCheck = (id: T, checked: boolean) => { | |
| 18 | + setCheckedIds((prev) => { | |
| 19 | + if (checked) { | |
| 20 | + return prev.includes(id) ? prev : [...prev, id]; | |
| 21 | + } | |
| 22 | + | |
| 23 | + return prev.filter((checkedId) => checkedId !== id); | |
| 24 | + }); | |
| 25 | + }; | |
| 26 | + | |
| 27 | + const handleCheckAll = (checked: boolean) => { | |
| 28 | + setCheckedIds(checked ? ids : []); | |
| 29 | + }; | |
| 30 | + | |
| 31 | + const resetChecked = () => { | |
| 32 | + setCheckedIds([]); | |
| 33 | + }; | |
| 34 | + | |
| 35 | + return { | |
| 36 | + checkedIds: currentCheckedIds, | |
| 37 | + isAllChecked, | |
| 38 | + isPartiallyChecked, | |
| 39 | + isChecked, | |
| 40 | + handleCheck, | |
| 41 | + handleCheckAll, | |
| 42 | + resetChecked, | |
| 43 | + }; | |
| 44 | +}; |
--- src/admin/route/AdminRoute.tsx
+++ src/admin/route/AdminRoute.tsx
... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 |
import {BoardListPage} from "../feature/board/page/BoardListPage.tsx";
|
| 3 | 3 |
import {ADMIN_BBS_MASTER_ROUTE} from "./adminRouteMap.ts";
|
| 4 | 4 |
import {BoardArticleListPage} from "../feature/board/page/BoardArticleListPage.tsx";
|
| 5 |
+import {BoardFormPage} from "../feature/board/page/BoardFormPage.tsx";
|
|
| 5 | 6 |
|
| 6 | 7 |
const ReadyPage = () => {
|
| 7 | 8 |
return <div>Preparing menu.</div>; |
... | ... | @@ -10,10 +11,12 @@ |
| 10 | 11 |
export const AdminRoute = () => {
|
| 11 | 12 |
return ( |
| 12 | 13 |
<Routes> |
| 13 |
- <Route path="/" element={<Navigate to={ADMIN_BBS_MASTER_ROUTE} replace />} />
|
|
| 14 |
- <Route path={ADMIN_BBS_MASTER_ROUTE} element={<BoardListPage />} />
|
|
| 15 |
- <Route path={`/detail/cop/bbs/article/:bbsId`} element={<BoardArticleListPage />}/>
|
|
| 16 |
- <Route path="*" element={<ReadyPage />} />
|
|
| 14 |
+ <Route path="/" element={<Navigate to={ADMIN_BBS_MASTER_ROUTE} replace/>}/>
|
|
| 15 |
+ <Route path={ADMIN_BBS_MASTER_ROUTE} element={<BoardListPage/>}/>
|
|
| 16 |
+ <Route path={`/admin/cop/bbs/article/:bbsId`} element={<BoardArticleListPage/>}/>
|
|
| 17 |
+ <Route path={`/admin/cop/bbs/detail/:bbsId`} element={<BoardFormPage/>}/>
|
|
| 18 |
+ <Route path={`/admin/cop/bbs/detail`} element={<BoardFormPage/>}/>
|
|
| 19 |
+ <Route path="*" element={<ReadyPage/>}/>
|
|
| 17 | 20 |
</Routes> |
| 18 | 21 |
); |
| 19 | 22 |
}; |
--- src/admin/route/adminRouteMap.ts
+++ src/admin/route/adminRouteMap.ts
... | ... | @@ -1,11 +1,13 @@ |
| 1 | 1 |
export const ADMIN_ROUTE_PREFIX = '/admin'; |
| 2 | 2 |
|
| 3 |
+export const ADMIN_BBS_MASTER_ROUTE = `${ADMIN_ROUTE_PREFIX}/cop/bbs/SelectBBSMasterInfs.do`;
|
|
| 4 |
+export const ADMIN_BBS_ARTICLE_LIST_ROUTE = `${ADMIN_ROUTE_PREFIX}/cop/bbs/article/`;
|
|
| 5 |
+export const ADMIN_BBS_ARTICLE_DETAIL_ROUTE = `${ADMIN_ROUTE_PREFIX}/cop/bbs/detail/`;
|
|
| 3 | 6 |
export const ADMIN_MENU_CREATE_TREE_ROUTE = `${ADMIN_ROUTE_PREFIX}/sym/mnu/mcm/EgovMenuCreatSelectJtree.do`;
|
| 4 | 7 |
export const ADMIN_AUTHOR_LIST_ROUTE = `${ADMIN_ROUTE_PREFIX}/sec/ram/EgovAuthorList.do`;
|
| 5 | 8 |
export const ADMIN_MAIN_ZONE_LIST_ROUTE = `${ADMIN_ROUTE_PREFIX}/uss/ion/pwm/mainZoneList.do`;
|
| 6 | 9 |
export const ADMIN_CONTENT_LIST_ROUTE = `${ADMIN_ROUTE_PREFIX}/uss/ion/cnt/contentList.do`;
|
| 7 | 10 |
export const ADMIN_MAIN_PAGE_ROUTE = `${ADMIN_ROUTE_PREFIX}/cmm/main/mainPage.do`;
|
| 8 |
-export const ADMIN_BBS_MASTER_ROUTE = `${ADMIN_ROUTE_PREFIX}/cop/bbs/SelectBBSMasterInfs.do`;
|
|
| 9 | 11 |
export const ADMIN_LOGIN_GROUP_POLICY_ROUTE = `${ADMIN_ROUTE_PREFIX}/uat/uap/selectLoginGroupPolicyList.do`;
|
| 10 | 12 |
export const ADMIN_MEMBER_MANAGE_ROUTE = `${ADMIN_ROUTE_PREFIX}/uss/umt/EgovMberManage.do`;
|
| 11 | 13 |
export const ADMIN_POPUP_ZONE_LIST_ROUTE = `${ADMIN_ROUTE_PREFIX}/uss/ion/pwm/popupZoneList.do`;
|
+++ src/type/code.ts
... | ... | @@ -0,0 +1,15 @@ |
| 1 | +export interface CommonCodeItem { | |
| 2 | + clCode: string; | |
| 3 | + code: string; | |
| 4 | + codeDc: string; | |
| 5 | + codeId: string; | |
| 6 | + codeIdNM: string; | |
| 7 | + codeNm: string; | |
| 8 | + frstRegisterId:string | |
| 9 | + lastUpdusrId:string | |
| 10 | + searchSortCnd:string | |
| 11 | + searchSortOrd:string | |
| 12 | + sortNo:string | |
| 13 | + tempCodeId:string | |
| 14 | + useAt:string | |
| 15 | +}(No newline at end of file) |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?