메인페이지 디자인, 사원 관리 api 구현 완료 :push
@ -44,7 +44,7 @@ public class WebSecurityConfig {
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
)
|
||||
.authorizeHttpRequests(request -> request
|
||||
.requestMatchers("/", "/api/v1/auth/**", "/api/v1/search/**", "/file/**").permitAll()
|
||||
.requestMatchers("/", "/api/v1/auth/**", "/api/v1/search/**", "/file/**" , "/api/v1/menu/**").permitAll()
|
||||
.requestMatchers(HttpMethod.GET, "/api/v1/board/**", "/api/v1/user/*").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
|
@ -0,0 +1,30 @@
|
||||
package com.eogns.board_back.controller;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.eogns.board_back.dto.request.auth.SignUpRequestDto;
|
||||
import com.eogns.board_back.dto.response.auth.SignUpResponseDto;
|
||||
import com.eogns.board_back.service.AuthService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/menu/employee")
|
||||
@RequiredArgsConstructor
|
||||
|
||||
public class EmployeeController{
|
||||
private final AuthService authService;
|
||||
|
||||
@PostMapping("/sign-up")
|
||||
public ResponseEntity<? super SignUpResponseDto> signUp(
|
||||
@RequestBody @Valid SignUpRequestDto requestBody
|
||||
) {
|
||||
ResponseEntity<? super SignUpResponseDto> response = authService.signUp(requestBody);
|
||||
return response;
|
||||
}
|
||||
}
|
@ -52,6 +52,23 @@ const SIGN_IN_URL = () => `${API_DOMAIN}/auth/sign-in`;
|
||||
|
||||
const SIGN_UP_URL = () => `${API_DOMAIN}/auth/sign-up`;
|
||||
|
||||
const EMPLOYEE_SIGN_UP_URL = () => `${API_DOMAIN}/menu/employee/sign-up`;
|
||||
|
||||
export const employeeSignUpRequest = async (requestBody: SignUpRequestDto) => {
|
||||
const result = await axios
|
||||
.post(EMPLOYEE_SIGN_UP_URL(), requestBody)
|
||||
.then((response) => {
|
||||
const responseBody: SignUpResponseDto = response.data;
|
||||
return responseBody;
|
||||
})
|
||||
.catch((error) => {
|
||||
if (!error.response.data) return null;
|
||||
const responseBody: ResponseDto = error.response.data;
|
||||
return responseBody;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
export const signInRequest = async (requestBody: SignInRequestDto) => {
|
||||
const result = await axios
|
||||
.post(SIGN_IN_URL(), requestBody)
|
||||
|
BIN
board-front/src/assets/image/b2.jpg
Normal file
After Width: | Height: | Size: 272 KiB |
BIN
board-front/src/assets/image/t.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
board-front/src/assets/image/t2.png
Normal file
After Width: | Height: | Size: 29 KiB |
20
board-front/src/components/MenuItem/menuItem.css
Normal file
@ -0,0 +1,20 @@
|
||||
.header-menu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
color: #000; /* 메뉴 텍스트 색상 */
|
||||
font-weight: bold;
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
}
|
||||
|
||||
.header-menu-icon {
|
||||
width: 20px; /* 이미지 크기 조절 */
|
||||
height: 20px; /* 이미지 크기 조절 */
|
||||
margin-right: 8px; /* 텍스트와 이미지 사이의 간격 조절 */
|
||||
}
|
||||
|
||||
.header-menu:hover {
|
||||
background-color: #f0f0f0; /* 호버 시 배경 색상 */
|
||||
color: #007bff; /* 호버 시 텍스트 색상 */
|
||||
}
|
13
board-front/src/components/MenuItem/menuItem.js
Normal file
@ -0,0 +1,13 @@
|
||||
import React from "react";
|
||||
import "./menuItem.css"; // 스타일을 포함하는 CSS 파일을 import 합니다.
|
||||
|
||||
const MenuItem = ({ text, imageSrc, onClick }) => {
|
||||
return (
|
||||
<div className="header-menu" onClick={onClick}>
|
||||
<img className="header-menu-icon" src={imageSrc} alt={`${text}`} />
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MenuItem;
|
@ -1,12 +1,44 @@
|
||||
import BoardDetail from "views/Board/Detail";
|
||||
|
||||
// 메인 페이지 경로를 반환하는 함수
|
||||
export const MAIN_PATH = () => "/";
|
||||
|
||||
// 인증 관련 페이지 경로를 반환하는 함수
|
||||
export const AUTH_PATH = () => "/auth";
|
||||
|
||||
// 검색 결과 페이지 경로를 반환하는 함수
|
||||
// 검색어를 path 변수로 포함
|
||||
export const SEARCH_PATH = (searchWord: string) => `/search/${searchWord}`;
|
||||
|
||||
// 특정 사용자 페이지 경로를 반환하는 함수
|
||||
// 사용자 이메일을 path 변수로 포함
|
||||
export const USER_PATH = (userEmail: string) => `/user/${userEmail}`;
|
||||
|
||||
// 게시판 경로를 반환하는 함수
|
||||
export const BOARD_PATH = () => "/board";
|
||||
|
||||
// 특정 게시물 상세 페이지 경로를 반환하는 함수
|
||||
// 게시물 번호를 path 변수로 포함
|
||||
export const BOARD_DETAIL_PATH = (boardNumber: string | number) =>
|
||||
`detail/${boardNumber}`;
|
||||
|
||||
// 게시물 작성 페이지 경로를 반환하는 함수
|
||||
export const BOARD_WRITE_PATH = () => "write";
|
||||
|
||||
// 특정 게시물 수정 페이지 경로를 반환하는 함수
|
||||
// 게시물 번호를 path 변수로 포함
|
||||
export const BOARD_UPDATE_PATH = (boardNumber: string | number) =>
|
||||
`update/${boardNumber}`;
|
||||
|
||||
// 사원 관리 페이지 경로를 반환하는 함수
|
||||
export const EMPLOYEE_MANAGEMENT_PATH = () => "/employee/sign-up";
|
||||
|
||||
// 권한 관리 페이지 경로를 반환하는 함수
|
||||
export const PERMISSION_MANAGEMENT_PATH = () => "/menu/permisson";
|
||||
|
||||
// 공지사항 페이지 경로를 반환하는 함수
|
||||
export const NOTICE_PATH = () => "/menu/notice";
|
||||
|
||||
/* EMPLOYEE_MANAGEMENT_PATH,
|
||||
PERMISSION_MANAGEMENT_PATH,
|
||||
NOTICE_PATH */
|
||||
|
@ -10,8 +10,10 @@ import {
|
||||
MAIN_PATH,
|
||||
SEARCH_PATH,
|
||||
USER_PATH,
|
||||
EMPLOYEE_MANAGEMENT_PATH,
|
||||
PERMISSION_MANAGEMENT_PATH,
|
||||
NOTICE_PATH,
|
||||
} from "constant";
|
||||
import { keyboardKey } from "@testing-library/user-event";
|
||||
import { useCookies } from "react-cookie";
|
||||
import { useBoardStore, useLoginuserStore } from "stores";
|
||||
import { fileUploadRequest, patchBoardRequest, postBoardRequest } from "apis";
|
||||
@ -21,83 +23,61 @@ import {
|
||||
PostBoardResponseDto,
|
||||
} from "apis/response/board";
|
||||
import { ResponseDto } from "apis/response";
|
||||
import MenuItem from "components/MenuItem/menuItem";
|
||||
|
||||
// component: 헤더 레이아웃 //
|
||||
// 헤더 레이아웃
|
||||
export default function Header() {
|
||||
// state: cookie 상태 //
|
||||
// 상태 정의
|
||||
const [cookies, setCookie] = useCookies();
|
||||
|
||||
// state: path 상태 //
|
||||
const { pathname } = useLocation();
|
||||
|
||||
// state: 로그인 유저 상태 //
|
||||
const { loginUser, setLoginUser, resetLoginUser } = useLoginuserStore();
|
||||
|
||||
// state: 로그인 상태 //
|
||||
const [isLogin, setLogin] = useState<boolean>(false);
|
||||
|
||||
// state: 인증 페이지 상태 //
|
||||
const [isAuthPage, setAuthPage] = useState<boolean>(false);
|
||||
|
||||
// state: 메인 페이지 상태 //
|
||||
const [isMainpage, setMainpage] = useState<boolean>(false);
|
||||
|
||||
// state: 검색 상태 //
|
||||
const [isSearchPage, setSearchPage] = useState<boolean>(false);
|
||||
|
||||
// state: 게시물 상세 페이지 상태 //
|
||||
const [isBoardDetailPage, setBoardDetailPage] = useState<boolean>(false);
|
||||
|
||||
// state: 게시물 작성 페이지 상태 //
|
||||
const [isBoardWritePage, setBoardWritePage] = useState<boolean>(false);
|
||||
|
||||
// state: 게시물 수정 페이지 상태 //
|
||||
const [isBoardUPdatePage, setBoardUPdatePage] = useState<boolean>(false);
|
||||
|
||||
// state: 유저 페이지 상태 //
|
||||
const [isUserPage, setUserPage] = useState<boolean>(false);
|
||||
|
||||
// function: 네비게이트 함수 //
|
||||
const [isEmployeePage, setEmployeePage] = useState<boolean>(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
// event handler: 로고 클릭 이벤트 처리 함수 //
|
||||
// 네비게이트 함수
|
||||
const onLogClickHandler = () => {
|
||||
navigate(MAIN_PATH());
|
||||
};
|
||||
|
||||
// component: 검색 버튼 컴포넌트 //
|
||||
// 메뉴 클릭 핸들러
|
||||
const onMenuClickHandler = (path: string) => {
|
||||
alert(path);
|
||||
navigate(path);
|
||||
};
|
||||
|
||||
const onMenuEmployeeClickHandler = () => {
|
||||
navigate(PERMISSION_MANAGEMENT_PATH());
|
||||
};
|
||||
|
||||
// 검색 버튼 컴포넌트
|
||||
const SearchButton = () => {
|
||||
// state: 검색 버튼 요소 참조 상태 //
|
||||
const searchButtonRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
// state: 검색 버튼 상태 //
|
||||
const [status, setStatus] = useState<boolean>(false);
|
||||
|
||||
// state: 검색어 상태 //
|
||||
const [word, setWord] = useState<string>("");
|
||||
|
||||
// state: 검색어 path variavble상태 //
|
||||
const { searchWord } = useParams();
|
||||
|
||||
// event Handler: 검색어 변경 이벤트 처리함수 //
|
||||
const onSearchWordChangeHandler = (
|
||||
event: ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
const value = event.target.value;
|
||||
setWord(value);
|
||||
setWord(event.target.value);
|
||||
};
|
||||
|
||||
// event Handler: 검색어 키 이벤트 처리함수 //
|
||||
const onSearchWordkeyDownHandler = (
|
||||
event: KeyboardEvent<HTMLInputElement>
|
||||
) => {
|
||||
if (event.key !== "Enter") return;
|
||||
if (!searchButtonRef.current) return;
|
||||
|
||||
searchButtonRef.current.click();
|
||||
};
|
||||
|
||||
// event Handler: 검색 버튼 클릭 이벤트 처리함수 //
|
||||
const onSearchButtonClickHandler = () => {
|
||||
if (!status) {
|
||||
setStatus(!status);
|
||||
@ -106,7 +86,6 @@ export default function Header() {
|
||||
navigate(SEARCH_PATH(word));
|
||||
};
|
||||
|
||||
// effect: 검색어 path varable이 변경 될 때 마다 실행될 함수 //
|
||||
useEffect(() => {
|
||||
if (searchWord) {
|
||||
setWord(searchWord);
|
||||
@ -114,14 +93,12 @@ export default function Header() {
|
||||
}
|
||||
}, [searchWord]);
|
||||
|
||||
// render: 검색 버튼 컴포넌트 렌더링(클릭 false) //
|
||||
if (!status)
|
||||
return (
|
||||
<div className="icon-button" onClick={onSearchButtonClickHandler}>
|
||||
<div className="icon search-light-icon"></div>
|
||||
</div>
|
||||
);
|
||||
// render: 검색 버튼 컴포넌트 렌더링(클릭 true) //
|
||||
return (
|
||||
<div className="header-search-input-box">
|
||||
<input
|
||||
@ -143,37 +120,32 @@ export default function Header() {
|
||||
);
|
||||
};
|
||||
|
||||
// component: 마이페이지 버튼 컴포넌트 //
|
||||
// 마이페이지 버튼 컴포넌트
|
||||
const MyPageButton = () => {
|
||||
// state: 유저 이메일 path variable 상태 //
|
||||
const { userEmail } = useParams();
|
||||
|
||||
// event Handler: 마이페이지 버튼 클릭 이벤트 처리 함수 //
|
||||
const onMyPageButtonClickHandler = () => {
|
||||
if (!loginUser) return;
|
||||
const { email } = loginUser;
|
||||
navigate(USER_PATH(email));
|
||||
};
|
||||
|
||||
// event Handler: 로그아웃 버튼 클릭 이벤트 처리 함수 //
|
||||
const onSignOutButtonClickHandler = () => {
|
||||
resetLoginUser();
|
||||
setCookie("accessToken", "", { path: MAIN_PATH(), expires: new Date() });
|
||||
navigate(MAIN_PATH());
|
||||
};
|
||||
|
||||
// event Handler: 로그인 버튼 클릭 이벤트 처리 함수 //
|
||||
const onSignInButtonClickHandler = () => {
|
||||
navigate(AUTH_PATH());
|
||||
};
|
||||
// render: 로그아웃 버튼 컴포넌트 렌더링 //
|
||||
|
||||
if (isLogin && userEmail === loginUser?.email)
|
||||
return (
|
||||
<div className="white-button" onClick={onSignOutButtonClickHandler}>
|
||||
{"로그아웃"}
|
||||
</div>
|
||||
);
|
||||
// render: 마이페이지 버튼 컴포넌트 렌더링 //
|
||||
if (isLogin)
|
||||
return (
|
||||
<div className="white-button" onClick={onMyPageButtonClickHandler}>
|
||||
@ -181,7 +153,6 @@ export default function Header() {
|
||||
</div>
|
||||
);
|
||||
|
||||
// render: 로그인 버튼 컴포넌트 렌더링 //
|
||||
return (
|
||||
<div className="black-button" onClick={onSignInButtonClickHandler}>
|
||||
{"로그인"}
|
||||
@ -189,14 +160,11 @@ export default function Header() {
|
||||
);
|
||||
};
|
||||
|
||||
// component: 업로드 버튼 컴포넌트 //
|
||||
// 업로드 버튼 컴포넌트
|
||||
const UploadButton = () => {
|
||||
// state: 게시물 번호 path variable 상태 //
|
||||
const { boardNumber } = useParams();
|
||||
// state: 게시물 상태 //
|
||||
const { title, content, boardImageFileList, resetBoard } = useBoardStore();
|
||||
|
||||
// function: post board response 처리 함수 //
|
||||
const postBoardResponse = (
|
||||
responseBody: PostBoardResponseDto | ResponseDto | null
|
||||
) => {
|
||||
@ -213,7 +181,6 @@ export default function Header() {
|
||||
navigate(USER_PATH(email));
|
||||
};
|
||||
|
||||
// function: patch board response 처리 함수 //
|
||||
const patchBoardResponse = (
|
||||
responseBody: PatchBoardResponseDto | ResponseDto | null
|
||||
) => {
|
||||
@ -228,7 +195,7 @@ export default function Header() {
|
||||
if (!boardNumber) return;
|
||||
navigate(BOARD_PATH() + "/" + BOARD_DETAIL_PATH(boardNumber));
|
||||
};
|
||||
// event Handler: 업로드 클릭 이벤트 처리 함수 //
|
||||
|
||||
const onUploadButtonClickHandler = async () => {
|
||||
const accessToken = cookies.accessToken;
|
||||
if (!accessToken) return;
|
||||
@ -271,47 +238,62 @@ export default function Header() {
|
||||
postBoardRequest(requestBody, accessToken).then(postBoardResponse);
|
||||
};
|
||||
|
||||
// render: 업로드 버튼 컴포넌트 렌더링 //
|
||||
if (title && content)
|
||||
return (
|
||||
<div className="black-button" onClick={onUploadButtonClickHandler}>
|
||||
{"업로드1"}
|
||||
{"업로드"}
|
||||
</div>
|
||||
);
|
||||
// render: 업로드 불가 버튼 컴포넌트 렌더링 //
|
||||
return <div className="disable-button">{"업로드 불가"}</div>;
|
||||
};
|
||||
|
||||
// effect: path가 변경될 때마다 실행될 함수 //
|
||||
useEffect(() => {
|
||||
const isAuthPage = pathname.startsWith(AUTH_PATH());
|
||||
setAuthPage(isAuthPage);
|
||||
|
||||
const isMainpage = pathname === MAIN_PATH();
|
||||
setMainpage(isMainpage);
|
||||
|
||||
const isSearchPage = pathname.startsWith(SEARCH_PATH(""));
|
||||
setSearchPage(isSearchPage);
|
||||
|
||||
const isBoardDetailPage = pathname.startsWith(
|
||||
BOARD_PATH() + "/" + BOARD_DETAIL_PATH("")
|
||||
);
|
||||
setBoardDetailPage(isBoardDetailPage);
|
||||
|
||||
const isBoardWritePage = pathname.startsWith(
|
||||
BOARD_PATH() + "/" + BOARD_WRITE_PATH()
|
||||
);
|
||||
setBoardWritePage(isBoardWritePage);
|
||||
|
||||
const isBoardUPdatePage = pathname.startsWith(
|
||||
BOARD_PATH() + "/" + BOARD_UPDATE_PATH("")
|
||||
);
|
||||
setBoardUPdatePage(isBoardUPdatePage);
|
||||
|
||||
const isUserPage = pathname.startsWith(USER_PATH(""));
|
||||
setUserPage(isUserPage);
|
||||
|
||||
const isEmployeePage = pathname.startsWith(EMPLOYEE_MANAGEMENT_PATH());
|
||||
setEmployeePage(isEmployeePage);
|
||||
}, [pathname]);
|
||||
|
||||
// effect: 로그인 유저가 변경될 때마다 실행될 함수 //
|
||||
useEffect(() => {
|
||||
setLogin(loginUser !== null);
|
||||
}, [loginUser]);
|
||||
|
||||
// render: 헤더 레이아웃 렌더링 //
|
||||
// state: 서브 메뉴바 상태 //
|
||||
const [showSubMenu, setShowSubMenu] = useState(null);
|
||||
|
||||
const handleMouseEnter = (menu: any) => {
|
||||
setShowSubMenu(menu);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setShowSubMenu(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div id="header">
|
||||
<div className="header-container">
|
||||
@ -321,13 +303,71 @@ export default function Header() {
|
||||
</div>
|
||||
<div className="header-logo">{"Layouts Header"}</div>
|
||||
</div>
|
||||
{isLogin && (
|
||||
<div className="header-menu-box">
|
||||
<div
|
||||
className="header-menu"
|
||||
onMouseEnter={() => handleMouseEnter("employee")}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<MenuItem
|
||||
text="사원 관리"
|
||||
imageSrc="https://cdn-icons-png.flaticon.com/512/159/159833.png"
|
||||
onClick={() => onMenuEmployeeClickHandler()}
|
||||
/>
|
||||
{showSubMenu === "employee" && (
|
||||
<div className="sub-menu">
|
||||
<div>사원 추가</div>
|
||||
<div>사원 목록</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="header-menu"
|
||||
onMouseEnter={() => handleMouseEnter("permission")}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<MenuItem
|
||||
text="권한 관리"
|
||||
imageSrc="https://cdn.iconscout.com/icon/premium/png-256-thumb/access-granted-3192235-2660379.png?f=webp"
|
||||
onClick={() => onMenuClickHandler(PERMISSION_MANAGEMENT_PATH())}
|
||||
/>
|
||||
{showSubMenu === "permission" && (
|
||||
<div className="sub-menu">
|
||||
<div>권한 추가</div>
|
||||
<div>권한 목록</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="header-menu"
|
||||
onMouseEnter={() => handleMouseEnter("notice")}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<MenuItem
|
||||
text="공지 사항"
|
||||
imageSrc="https://png.pngtree.com/png-vector/20190118/ourmid/pngtree-vector-announcement-icon-png-image_323832.jpg"
|
||||
onClick={() => onMenuClickHandler(NOTICE_PATH())}
|
||||
/>
|
||||
{showSubMenu === "notice" && (
|
||||
<div className="sub-menu">
|
||||
<div>공지 추가</div>
|
||||
<div>공지 목록</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="header-right-box">
|
||||
{(isAuthPage || isMainpage || isSearchPage || isBoardDetailPage) && (
|
||||
<SearchButton />
|
||||
)}
|
||||
{(isMainpage || isSearchPage || isBoardDetailPage || isUserPage) && (
|
||||
<MyPageButton />
|
||||
)}
|
||||
{(isMainpage ||
|
||||
isSearchPage ||
|
||||
isBoardDetailPage ||
|
||||
isUserPage ||
|
||||
isEmployeePage) && <MyPageButton />}
|
||||
{(isBoardWritePage || isBoardUPdatePage) && <UploadButton />}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,8 +1,8 @@
|
||||
#header {
|
||||
padding: 20px 120px;
|
||||
|
||||
padding: 20px 200px;
|
||||
display: flex;
|
||||
justify-items: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
@ -63,3 +63,77 @@
|
||||
font-weight: 400;
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.header-menu-box {
|
||||
margin-left: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: auto; /* to align menus to the left */
|
||||
}
|
||||
|
||||
.header-menu {
|
||||
position: relative;
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
color: #000; /* menu text color */
|
||||
font-weight: bold;
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
}
|
||||
|
||||
.header-menu:hover {
|
||||
background-color: #f0f0f0; /* background color on hover */
|
||||
color: #007bff; /* text color on hover */
|
||||
}
|
||||
|
||||
.sub-menu {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
top: 100%; /* 부모 메뉴 아래에 위치 */
|
||||
left: 0;
|
||||
margin-top: 5px; /* 간격 조정 */
|
||||
padding: 10px 0;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sub-menu div {
|
||||
cursor: pointer;
|
||||
padding: 8px 16px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.sub-menu div:hover {
|
||||
background-color: #f0f0f0;
|
||||
color: #007bff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sub-menu div + div {
|
||||
border-top: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.sub-menu div {
|
||||
cursor: pointer;
|
||||
padding: 8px 16px;
|
||||
transition: background-color 0.3s, color 0.3s; /* 색깔 변화에 대한 트랜지션 추가 */
|
||||
}
|
||||
|
||||
.sub-menu div:hover {
|
||||
background-color: #f0f0f0;
|
||||
color: #007bff; /* 호버 시 글자 색깔 변경 */
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 서브 메뉴 항목의 기본 색상 설정 */
|
||||
.sub-menu div {
|
||||
color: #333; /* 기본 글자 색 */
|
||||
}
|
||||
|
||||
/* 서브 메뉴 항목 호버 시 색상 변경 */
|
||||
.sub-menu div:hover {
|
||||
color: #007bff; /* 원하는 색깔로 변경 */
|
||||
}
|
||||
|
BIN
board-front/src/views/Authentication/assets/t.png
Normal file
After Width: | Height: | Size: 27 KiB |
@ -134,6 +134,7 @@ export default function Authentication() {
|
||||
<div className="auth-card">
|
||||
<div className="auth-card-box">
|
||||
<div className="auth-card-top">
|
||||
<div className="auth-card-welcome">{"환영합니다!"}</div>
|
||||
<div className="auth-card-title-box">
|
||||
<div className="auth-card-title">{"로그인"}</div>
|
||||
</div>
|
||||
@ -763,10 +764,8 @@ export default function Authentication() {
|
||||
<div className="auth-jumbotron-contents">
|
||||
<div className="auth-logo-icon"></div>
|
||||
<div className="auth-jumbotron-text-box">
|
||||
<div className="auth-jumbotron-text">{"환영합니다"}</div>
|
||||
<div className="auth-jumbotron-text">
|
||||
{"홈페이지 만들기 TEST"}
|
||||
</div>
|
||||
<div className="auth-jumbotron-text">{""}</div>
|
||||
<div className="auth-jumbotron-text">{""}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,10 +8,11 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
background-image: url(assets/auth-background.png);
|
||||
background-position: 50% 50%;
|
||||
background-image: url(assets/t.png);
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
background-position: 15% 25%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: auto;
|
||||
background-size: 60% 60%;
|
||||
}
|
||||
|
||||
.auth-container {
|
||||
@ -95,6 +96,14 @@
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.auth-card-welcome {
|
||||
color: rgba(0, 0, 0, 1);
|
||||
|
||||
font-size: 24px;
|
||||
font-weight: 300;
|
||||
line-height: 100%;
|
||||
}
|
||||
|
||||
.auth-card-title {
|
||||
color: rgba(0, 0, 0, 1);
|
||||
|
||||
|
BIN
board-front/src/views/Main/assets/t2.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
board-front/src/views/Main/assets/t3.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
board-front/src/views/Main/assets/t4.jpg
Normal file
After Width: | Height: | Size: 886 KiB |
BIN
board-front/src/views/Main/assets/t5.avif
Normal file
After Width: | Height: | Size: 4.0 KiB |
@ -2,15 +2,15 @@ import React, { useEffect, useState } from "react";
|
||||
import "./style.css";
|
||||
import Top3Item from "components/Top3Item";
|
||||
import { BoardListItem } from "types/interface";
|
||||
import { latestBoardListMock, top3BoardListMock } from "mocks";
|
||||
import BoardItem from "components/BoardItem";
|
||||
import Pagination from "components/Pagination";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { SEARCH_PATH } from "constant";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { MAIN_PATH, SEARCH_PATH } from "constant";
|
||||
import {
|
||||
getLatestBoardListRequset,
|
||||
getPopularListRequest,
|
||||
getTop3BoardListRequest,
|
||||
getUserRequest,
|
||||
} from "apis";
|
||||
import {
|
||||
GetLatestBoardListResponseDto,
|
||||
@ -19,6 +19,11 @@ import {
|
||||
import { ResponseDto } from "apis/response";
|
||||
import { usePagination } from "hooks";
|
||||
import { GetPoplarListResponseDto } from "apis/response/search";
|
||||
import { useCookies } from "react-cookie";
|
||||
import { GetUserResponseDto } from "apis/response/user";
|
||||
import { useLoginuserStore } from "stores";
|
||||
|
||||
import DefaultProfileImage from "assets/image/default-profile-image.png";
|
||||
|
||||
// component 메인 화면 컴포넌트 //
|
||||
export default function Main() {
|
||||
@ -30,6 +35,24 @@ export default function Main() {
|
||||
// state: 주간 top3 게시물 리스트 상태 //
|
||||
const [top3BoardList, setTop3BoardList] = useState<BoardListItem[]>([]);
|
||||
|
||||
// state: 로그인 상태 //
|
||||
const [isLogin, setLogin] = useState<boolean>(false);
|
||||
|
||||
// state: 로그인 유저 상태 //
|
||||
const { loginUser } = useLoginuserStore();
|
||||
|
||||
// state: 쿠키 상태 //
|
||||
const [cookies, setCookies] = useCookies();
|
||||
|
||||
// state: userEamil path variable 여부 상태 //
|
||||
const { userEmail } = useParams();
|
||||
|
||||
// state: 닉네임 상태 //
|
||||
const [nickname, setNickname] = useState<string>("");
|
||||
|
||||
// state: 프로필 이미지 상태 //
|
||||
const [profileImage, setProfileImage] = useState<string | null>(null);
|
||||
|
||||
// function: getTop3BoardListResponse 처리 함수 //
|
||||
const getTop3BoardListResponse = (
|
||||
responseBody: GetTop3BoardListResponseDto | ResponseDto | null
|
||||
@ -43,23 +66,102 @@ export default function Main() {
|
||||
setTop3BoardList(top3List);
|
||||
};
|
||||
|
||||
// function: getUserResponse 처리 함수 //
|
||||
const getUserResponse = (
|
||||
responseBody: GetUserResponseDto | ResponseDto | null
|
||||
) => {
|
||||
if (!responseBody) return;
|
||||
const { code } = responseBody;
|
||||
if (code === "NU") alert("존재하지 않는 유저입니다.");
|
||||
if (code === "DBE") alert("데이터베이스 오류입니다");
|
||||
if (code !== "SU") {
|
||||
navigate(MAIN_PATH());
|
||||
return;
|
||||
}
|
||||
const { email, nickname, profileImage } =
|
||||
responseBody as GetUserResponseDto;
|
||||
setNickname(nickname);
|
||||
setProfileImage(profileImage);
|
||||
/* const isMyPage = email === loginUser?.email;
|
||||
setMyPage(isMyPage); */
|
||||
};
|
||||
|
||||
// effect: 첫 마운트 시 실행될 함수 //
|
||||
useEffect(() => {
|
||||
getTop3BoardListRequest().then(getTop3BoardListResponse);
|
||||
}, []);
|
||||
|
||||
// effect: accessToken cookie 값이 변경될 때 마다 실행할 함수-------------- //
|
||||
useEffect(() => {
|
||||
const accessToken = cookies.accessToken;
|
||||
if (accessToken !== null) {
|
||||
setLogin(true);
|
||||
} else {
|
||||
setLogin(false); // accessToken이 없으면 로그인 상태를 false로 설정
|
||||
}
|
||||
}, [cookies]);
|
||||
|
||||
// effect: user email path variable 변경시 실행 할 함수 //
|
||||
useEffect(() => {
|
||||
if (!loginUser) return; // loginUser가 없으면 함수 실행 중단
|
||||
const { email } = loginUser; // loginUser에서 email 추출
|
||||
getUserRequest(email).then(getUserResponse);
|
||||
console.log(email);
|
||||
}, [loginUser]); // loginUser가 변경될 때마다 실행
|
||||
|
||||
// render 메인화면 상단 컴포넌트 렌더링 //
|
||||
return (
|
||||
<div id="main-top-wrapper">
|
||||
<div className="main-top-container">
|
||||
<div className="main-top-title">{"Project Demo\n-Main Page-"}</div>
|
||||
<div className="main-top-contents-box">
|
||||
<div className="main-top-contents-title">{"주간 TOP3 게시글"}</div>
|
||||
<div className="main-top-contents">
|
||||
{top3BoardList.map((top3ListItem) => (
|
||||
<Top3Item top3ListItem={top3ListItem} />
|
||||
))}
|
||||
{loginUser && (
|
||||
<div className="main-work-wrapper">
|
||||
<div className="main-top-contents-box1">
|
||||
<div className="main-top-contents-title">{"주간 게시글"}</div>
|
||||
<div className="main-top-contents">
|
||||
{top3BoardList.slice(0, 2).map((top3ListItem) => (
|
||||
<Top3Item top3ListItem={top3ListItem} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="main-work-container">
|
||||
<div
|
||||
className="profile-box"
|
||||
style={{
|
||||
backgroundImage: `url(${
|
||||
profileImage ? profileImage : DefaultProfileImage
|
||||
})`,
|
||||
}}
|
||||
></div>
|
||||
|
||||
<div className="welcome">
|
||||
<span style={{ fontWeight: "bold" }}>{nickname}</span>님
|
||||
환영합니다.
|
||||
</div>
|
||||
<div className="button-box">
|
||||
<div className="work">{"출근"}</div>
|
||||
<div className="work-out">{"퇴근"}</div>
|
||||
<div className="vacation">{"휴가"}</div>
|
||||
<div className="work-other-place">{"출장"}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loginUser && (
|
||||
<>
|
||||
<div className="main-top-title">
|
||||
{"Project Demo\n-Main Page-"}
|
||||
</div>
|
||||
<div className="main-top-contents-box">
|
||||
<div className="main-top-contents-title">{"주간 게시글"}</div>
|
||||
<div className="main-top-contents">
|
||||
{top3BoardList.map((top3ListItem) => (
|
||||
<Top3Item top3ListItem={top3ListItem} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -121,7 +223,7 @@ export default function Main() {
|
||||
return (
|
||||
<div id="main-bottom-wrapper">
|
||||
<div className="main-bottom-container">
|
||||
<div className="main-bottom-title">{"최신 게시물"}</div>
|
||||
<div className="main-bottom-title">{"공지 사항 리스트"}</div>
|
||||
<div className="main-bottom-contents-box">
|
||||
<div className="main-bottom-current-contents">
|
||||
{viewList.map((boardListItem) => (
|
||||
|
@ -1,8 +1,14 @@
|
||||
#main-top-wrapper {
|
||||
padding: 80px 0 40px;
|
||||
padding: 40px 0 40px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
background-image: url(assets/t5.avif);
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
background-position: 50% 40%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.main-top-container {
|
||||
@ -14,6 +20,102 @@
|
||||
align-items: center;
|
||||
gap: 56px;
|
||||
}
|
||||
.main-work-wrapper {
|
||||
padding: 0 0 45px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.main-work-container {
|
||||
margin-left: -1000px;
|
||||
width: 350px;
|
||||
min-width: 350px;
|
||||
min-height: 150px;
|
||||
|
||||
border: none; /* 테두리 */
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column; /* 세로 배치 */
|
||||
align-items: center;
|
||||
|
||||
gap: 20px;
|
||||
padding: 20px 0; /* 위 아래 패딩 추가 */
|
||||
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.profile-box {
|
||||
border-radius: 50%;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
background-color: rgba(217, 217, 217, 1);
|
||||
background-position: 50% 50%;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
font-size: 18px;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
max-width: 200px; /* 최대 너비 200px 설정 */
|
||||
overflow-wrap: break-word; /* 긴 단어가 줄바꿈되도록 설정 */
|
||||
}
|
||||
|
||||
.button-box {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr); /* 2열 */
|
||||
gap: 10px; /* 버튼들 사이의 간격 */
|
||||
}
|
||||
|
||||
.button-box div {
|
||||
border-radius: 46px;
|
||||
width: 95px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: rgba(0, 0, 0, 1);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 140%;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.button-box div:hover {
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
/* 각 버튼의 위치 */
|
||||
.work {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.work-out {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.vacation {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.work-other-place {
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.main-top-title {
|
||||
color: rgba(0, 0, 0, 1);
|
||||
@ -28,6 +130,14 @@
|
||||
white-space: pre-wrap; /* 줄바꿈 설정 */
|
||||
}
|
||||
|
||||
.main-top-contents-box1 {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.main-top-contents-box {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
BIN
board-front/src/views/Menu/employee/assets/auth-background.png
Normal file
After Width: | Height: | Size: 4.7 MiB |
BIN
board-front/src/views/Menu/employee/assets/auth-logo.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
596
board-front/src/views/Menu/employee/index.tsx
Normal file
@ -0,0 +1,596 @@
|
||||
import React, {
|
||||
useState,
|
||||
KeyboardEvent,
|
||||
useRef,
|
||||
ChangeEvent,
|
||||
useEffect,
|
||||
} from "react";
|
||||
import "./style.css";
|
||||
import InputBox from "components/InputBox";
|
||||
import { SignInRequestDto, SignUpRequestDto } from "apis/request/auth";
|
||||
import { signInRequest, signUpRequest, employeeSignUpRequest } from "apis";
|
||||
import { SignInResponseDto } from "apis/response/auth";
|
||||
import { ResponseDto } from "apis/response";
|
||||
import { useCookies } from "react-cookie";
|
||||
import { MAIN_PATH } from "constant";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Address, useDaumPostcodePopup } from "react-daum-postcode";
|
||||
import SignUpResponseDto from "apis/response/auth/sign-up-response.dto";
|
||||
|
||||
export default function employee() {
|
||||
/* // state: 화면 상태 //
|
||||
const [view, setView] = useState<"sign-in" | "sign-up">("sign-up"); */
|
||||
|
||||
/* // state: 쿠키 상태 //
|
||||
const [cookies, setCookie] = useCookies(); */
|
||||
|
||||
/* // function: 네비게이터 함수 //
|
||||
const navigate = useNavigate(); */
|
||||
|
||||
// component sign up card 컴포넌트 //
|
||||
const SignUpCard = () => {
|
||||
// state: 이메일 요소 참조 상태 //
|
||||
const emailRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
// state: 패스워드 요소 참조 상태 //
|
||||
const passwordRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
// state: 패스워드 확인 요소 참조 상태 //
|
||||
const passCheckRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
// state: 닉네임 요소 참조 상태 //
|
||||
const nicknameRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
// state: 전화번호 요소 참조 상태 //
|
||||
const telNumberRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
// state: 주소 요소 참조 상태 //
|
||||
const addressRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
// state: 상세 주소 요소 참조 상태 //
|
||||
const addressDetailRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
// state: 페이지 번호 상태 //
|
||||
const [page, setPage] = useState<1 | 2>(1);
|
||||
|
||||
// state: 이메일 상태 //
|
||||
const [email, setEmail] = useState<string>("");
|
||||
|
||||
// state: 패스워드 상태 //
|
||||
const [password, setPassword] = useState<string>("");
|
||||
|
||||
// state: 패스워드 확인 상태 //
|
||||
const [passCheck, setPassCheck] = useState<string>("");
|
||||
|
||||
// state: 닉네임 상태 //
|
||||
const [nickname, setNickname] = useState<string>("");
|
||||
|
||||
// state: 휴대폰 번호 상태 //
|
||||
const [telNumber, setTelNumber] = useState<string>("");
|
||||
|
||||
// state: 주소 상태 //
|
||||
const [address, setAddress] = useState<string>("");
|
||||
|
||||
// state: 상세주소 상태 //
|
||||
const [addressDetail, setAddressDetail] = useState<string>("");
|
||||
|
||||
// state: 개인 정보 동의 타입 상태 //
|
||||
const [agreedPersonal, setAgreedPersonal] = useState<boolean>(false);
|
||||
|
||||
// state: 패스워드 타입 상태 //
|
||||
const [passwordType, setPasswordType] = useState<"text" | "password">(
|
||||
"password"
|
||||
);
|
||||
|
||||
// state: 패스워드 확인 타입 상태 //
|
||||
const [passCheckType, setPassCheckType] = useState<"text" | "password">(
|
||||
"password"
|
||||
);
|
||||
// state: 이메일 에러 상태 //
|
||||
const [isemailError, setEmailError] = useState<boolean>(false);
|
||||
|
||||
// state: 패스워드 에러 상태 //
|
||||
const [isPasswordError, setPasswordError] = useState<boolean>(false);
|
||||
|
||||
// state: 패스워드 확인 에러 상태 //
|
||||
const [isPassCheckError, setPassCheckError] = useState<boolean>(false);
|
||||
|
||||
// state: 이메일 에러 메시지 상태 //
|
||||
const [emailErrorMessage, setEmailErrorMessage] = useState<string>("");
|
||||
|
||||
// state: 패스워드 에러 메시지 상태 //
|
||||
const [passwordErrorMessage, setPasswordErrorMessage] =
|
||||
useState<string>("");
|
||||
|
||||
// state: 패스워드 확인 에러 메시지 상태 //
|
||||
const [passCheckErrorMessage, setPassCheckErrorMessage] =
|
||||
useState<string>("");
|
||||
|
||||
// state: 닉네임 에러 상태 //
|
||||
const [isNicknameError, setNicknameError] = useState<boolean>(false);
|
||||
|
||||
// state: 휴대폰 번호 에러 상태 //
|
||||
const [isTelNumberError, setTelNumberError] = useState<boolean>(false);
|
||||
|
||||
// state: 주소 에러 상태 //
|
||||
const [isAddressError, setAddressError] = useState<boolean>(false);
|
||||
|
||||
// state: 개인 정보 동의 에러 상태 //
|
||||
const [isAgreedPersonalError, setAgreedPersonalError] =
|
||||
useState<boolean>(false);
|
||||
|
||||
// state: 닉네임 에러 메시지 상태 //
|
||||
const [nicknameErrorMessage, setNicknameErrorMessage] =
|
||||
useState<string>("");
|
||||
|
||||
// state: 휴대폰 에러 메시지 상태 //
|
||||
const [telNumberErrorMessage, setTelNumberErrorMessage] =
|
||||
useState<string>("");
|
||||
|
||||
// state: 주소 에러 메시지 상태 //
|
||||
const [addressErrorMessage, setAddressErrorMessage] = useState<string>("");
|
||||
|
||||
// state: 패스워드 버튼 아이콘 상태 //
|
||||
const [passwordButtonIcon, setPasswordButtonIcon] = useState<
|
||||
"eye-light-off-icon" | "eye-light-on-icon"
|
||||
>("eye-light-off-icon");
|
||||
|
||||
// state: 패스워드 확인 버튼 아이콘 상태 //
|
||||
const [passCheckButtonIcon, setPassCheckButtonIcon] = useState<
|
||||
"eye-light-off-icon" | "eye-light-on-icon"
|
||||
>("eye-light-off-icon");
|
||||
|
||||
// function: 다음 주소 검색 팝업 오픈 함수 //
|
||||
const open = useDaumPostcodePopup();
|
||||
|
||||
// function: sign up response 처리 함수 //
|
||||
const signUpResponse = (
|
||||
responsBody: SignUpResponseDto | ResponseDto | null
|
||||
) => {
|
||||
if (!responsBody) {
|
||||
alert("네트워크 이상입니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
const { code } = responsBody;
|
||||
if (code === "DE") {
|
||||
setEmailError(true);
|
||||
setEmailErrorMessage("중복되는 이메일 주소입니다.");
|
||||
}
|
||||
if (code === "DN") {
|
||||
setNicknameError(true);
|
||||
setNicknameErrorMessage("중복되는 닉네임입니다.");
|
||||
}
|
||||
if (code === "DT") {
|
||||
setTelNumberError(true);
|
||||
setTelNumberErrorMessage("중복되는 휴대폰 번호입니다.");
|
||||
}
|
||||
if (code === "VF") {
|
||||
alert("모든 값을 입력하세요.");
|
||||
}
|
||||
if (code === "DBE") {
|
||||
alert("데이터 베이스 오류입니다.");
|
||||
}
|
||||
if (code !== "SU") return;
|
||||
};
|
||||
|
||||
// event handler: 닉네임 변경 이벤트 처리 //
|
||||
const onNicknameChangeHandler = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const { value } = event.target;
|
||||
setNickname(value);
|
||||
setNicknameError(false);
|
||||
setNicknameErrorMessage("");
|
||||
};
|
||||
|
||||
// event handler: 휴대폰 번호 변경 이벤트 처리 //
|
||||
const onTelNumberChangeHandler = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const { value } = event.target;
|
||||
setTelNumber(value);
|
||||
setTelNumberError(false);
|
||||
setTelNumberErrorMessage("");
|
||||
};
|
||||
|
||||
// event handler: 주소 변경 변경 이벤트 처리 //
|
||||
const onAddressChangeHandler = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const { value } = event.target;
|
||||
setAddress(value);
|
||||
setAddressError(false);
|
||||
setAddressErrorMessage("");
|
||||
};
|
||||
|
||||
// event handler: 상세 주소 변경 이벤트 처리 //
|
||||
const onAddressDetailChangeHandler = (
|
||||
event: ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
const { value } = event.target;
|
||||
setAddressDetail(value);
|
||||
};
|
||||
// event handler: 개인 정보 동의 체크박스 클릭 이벤트 처리 //
|
||||
const onAgreedPersonalClickHandler = () => {
|
||||
setAgreedPersonal(!agreedPersonal);
|
||||
setAgreedPersonalError(false);
|
||||
};
|
||||
|
||||
// event handler: 패스워드 버튼 클릭 이벤트 처리 //
|
||||
const onPasswordButtonClickHandler = () => {
|
||||
if (passwordButtonIcon === "eye-light-off-icon") {
|
||||
setPasswordButtonIcon("eye-light-on-icon");
|
||||
setPasswordType("text");
|
||||
} else {
|
||||
setPasswordButtonIcon("eye-light-off-icon");
|
||||
setPasswordType("password");
|
||||
}
|
||||
};
|
||||
|
||||
// event handler: 패스워드 확인 버튼 클릭 이벤트 처리 //
|
||||
const onPassCheckButtonClickHandler = () => {
|
||||
if (passCheckButtonIcon === "eye-light-off-icon") {
|
||||
setPassCheckButtonIcon("eye-light-on-icon");
|
||||
setPassCheckType("text");
|
||||
} else {
|
||||
setPassCheckButtonIcon("eye-light-off-icon");
|
||||
setPassCheckType("password");
|
||||
}
|
||||
};
|
||||
|
||||
// event handler: 이메일 변경 이벤트 처리 //
|
||||
const onEmailChangeHandler = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const { value } = event.target;
|
||||
setEmail(value);
|
||||
setEmailError(false);
|
||||
setEmailErrorMessage("");
|
||||
};
|
||||
|
||||
// event handler: 패스워드 변경 이벤트 처리 //
|
||||
const onPasswordChangeHandler = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const { value } = event.target;
|
||||
setPassword(value);
|
||||
setPasswordError(false);
|
||||
setPasswordErrorMessage("");
|
||||
};
|
||||
|
||||
// event handler: 패스워드 확인 변경 이벤트 처리 //
|
||||
const onPassCheckChangeHandler = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const { value } = event.target;
|
||||
setPassCheck(value);
|
||||
setPassCheckError(false);
|
||||
setPassCheckErrorMessage("");
|
||||
};
|
||||
|
||||
// event Handler: 다음 버튼 클릭 이벤트 처리 //
|
||||
const onNextButtonClickHandler = () => {
|
||||
const emailPattern = /^[a-zA-Z0-9]*@([-.]?[a-zA-Z0-9])*\.[a-zA-Z]{2,4}$/;
|
||||
const isEmailPattern = emailPattern.test(email);
|
||||
if (!isEmailPattern) {
|
||||
setEmailError(true);
|
||||
setEmailErrorMessage("이메일 주소 포멧이 맞지 않습니다.");
|
||||
}
|
||||
const isCheckedPassword = password.trim().length >= 8;
|
||||
if (!isCheckedPassword) {
|
||||
setPasswordError(true);
|
||||
setPasswordErrorMessage("비밀번호는 8자 이상 입력해 주세요.");
|
||||
}
|
||||
const isEqualPassword = password === passCheck;
|
||||
if (!isEqualPassword) {
|
||||
setPassCheckError(true);
|
||||
setPassCheckErrorMessage("비밀번호가 일지하지 않습니다.");
|
||||
}
|
||||
|
||||
if (!isEmailPattern || !isCheckedPassword || !isEqualPassword) return;
|
||||
|
||||
setPage(2);
|
||||
};
|
||||
|
||||
// event handler: 회원가입 버튼 클릭 이벤트 처리 //
|
||||
const onSignUpButtonClickHandler = () => {
|
||||
const emailPattern = /^[a-zA-Z0-9]*@([-.]?[a-zA-Z0-9])*\.[a-zA-Z]{2,4}$/;
|
||||
const isEmailPattern = emailPattern.test(email);
|
||||
if (!isEmailPattern) {
|
||||
setEmailError(true);
|
||||
setEmailErrorMessage("이메일 주소 포멧이 맞지 않습니다.");
|
||||
}
|
||||
const isCheckedPassword = password.length >= 8;
|
||||
if (!isCheckedPassword) {
|
||||
setPasswordError(true);
|
||||
setPasswordErrorMessage("비밀번호는 8자 이상 입력해 주세요.");
|
||||
}
|
||||
const isEqualPassword = password === passCheck;
|
||||
if (!isEqualPassword) {
|
||||
setPassCheckError(true);
|
||||
setPassCheckErrorMessage("비밀번호가 일지하지 않습니다.");
|
||||
}
|
||||
if (!isEmailPattern || !isCheckedPassword || !isEqualPassword) {
|
||||
setPage(1);
|
||||
return;
|
||||
}
|
||||
const hasNickname = nickname.length > 0; /* nickname.trim().length 지움*/
|
||||
if (!hasNickname) {
|
||||
setNicknameError(true);
|
||||
setNicknameErrorMessage("닉네임을 입력해주세요.");
|
||||
}
|
||||
|
||||
const telNumberPattern = /^[0-9]{11,13}$/;
|
||||
const isTelNumberPattern = telNumberPattern.test(telNumber);
|
||||
if (!isTelNumberPattern) {
|
||||
setTelNumberError(true);
|
||||
setTelNumberErrorMessage("숫자만 입력해주세요.");
|
||||
}
|
||||
|
||||
const hasAddress = address.length > 0; /* address.trim().length 지움*/
|
||||
if (!hasAddress) {
|
||||
setAddressError(true);
|
||||
setAddressErrorMessage("주소를 선택해주세요");
|
||||
}
|
||||
if (!agreedPersonal) {
|
||||
setAgreedPersonalError(true);
|
||||
}
|
||||
|
||||
if (!hasNickname || !isTelNumberPattern || !agreedPersonal) return;
|
||||
|
||||
const requestBody: SignUpRequestDto = {
|
||||
email,
|
||||
password,
|
||||
nickname,
|
||||
telNumber,
|
||||
address,
|
||||
addressDetail,
|
||||
agreedPersonal,
|
||||
};
|
||||
|
||||
employeeSignUpRequest(requestBody).then(signUpResponse);
|
||||
};
|
||||
|
||||
// event handler: 로그인 링크 클릭 이벤트 처리 //
|
||||
const onSignInLinkClickHandler = () => {};
|
||||
|
||||
// event handler: 이메일 키 다운 이벤트 처리 //
|
||||
const onEmailKeyDownHandler = (event: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (event.key !== "Enter") return;
|
||||
if (!passwordRef.current) return;
|
||||
passwordRef.current.focus();
|
||||
};
|
||||
|
||||
// event handler: 패스워드키 다운 이벤트 처리 //
|
||||
const onPasswordKeyDownHandler = (
|
||||
event: KeyboardEvent<HTMLInputElement>
|
||||
) => {
|
||||
if (event.key !== "Enter") return;
|
||||
if (!passCheckRef.current) return;
|
||||
passCheckRef.current.focus();
|
||||
};
|
||||
|
||||
// event handler: 패스워드 확인 키 다운 이벤트 처리 //
|
||||
const onPassCheckKeyDownHandler = (
|
||||
event: KeyboardEvent<HTMLInputElement>
|
||||
) => {
|
||||
if (event.key !== "Enter") return;
|
||||
onNextButtonClickHandler();
|
||||
};
|
||||
|
||||
// event handler: 닉네임 키 다운 이벤트 처리 //
|
||||
const onNicknameKeyDownHandler = (
|
||||
event: KeyboardEvent<HTMLInputElement>
|
||||
) => {
|
||||
if (event.key !== "Enter") return;
|
||||
if (!telNumberRef.current) return;
|
||||
telNumberRef.current.focus();
|
||||
};
|
||||
|
||||
// event handler: 폰 번호 키 다운 이벤트 처리 //
|
||||
const onTelNumberKeyDownHandler = (
|
||||
event: KeyboardEvent<HTMLInputElement>
|
||||
) => {
|
||||
if (event.key !== "Enter") return;
|
||||
onAddressButtonClickHandler();
|
||||
};
|
||||
|
||||
// event handler: 주소 키 다운 이벤트 처리 //
|
||||
const onAddressKeyDownHandler = (
|
||||
event: KeyboardEvent<HTMLInputElement>
|
||||
) => {
|
||||
if (event.key !== "Enter") return;
|
||||
if (!addressDetailRef.current) return;
|
||||
addressDetailRef.current.focus();
|
||||
};
|
||||
|
||||
// event handler: 상세주소 키 다운 이벤트 처리 //
|
||||
const onAddressDetailKeyDownHandler = (
|
||||
event: KeyboardEvent<HTMLInputElement>
|
||||
) => {
|
||||
if (event.key !== "Enter") return;
|
||||
onSignUpButtonClickHandler();
|
||||
};
|
||||
|
||||
// event handler: 다음 주소 검색 완료 이벤트 처리 //
|
||||
const onComplete = (data: Address) => {
|
||||
const { address } = data;
|
||||
setAddress(address);
|
||||
setAddressError(false);
|
||||
setAddressErrorMessage("");
|
||||
if (!addressDetailRef.current) return;
|
||||
addressDetailRef.current.focus();
|
||||
};
|
||||
|
||||
// event Handler: 주소 버튼 클릭 이벤트 처리 //
|
||||
const onAddressButtonClickHandler = () => {
|
||||
open({ onComplete });
|
||||
};
|
||||
|
||||
// effect: 페이지가 변경될 때 마다 실행될 함수 //
|
||||
useEffect(() => {
|
||||
if (page == 2) {
|
||||
if (!nicknameRef.current) return;
|
||||
nicknameRef.current.focus();
|
||||
}
|
||||
}, [page]);
|
||||
|
||||
// render: Sign up card 컴포넌트 렌더링 //
|
||||
return (
|
||||
<div className="employee-card">
|
||||
<div className="employee-card-box">
|
||||
<div className="employee-card-top">
|
||||
<div className="employee-card-title-box">
|
||||
<div className="employee-card-title">{"사용자 계정 생성"}</div>
|
||||
<div className="employee-catd-page">{`${page}/2`}</div>
|
||||
</div>
|
||||
{page === 1 && (
|
||||
<>
|
||||
<InputBox
|
||||
ref={emailRef}
|
||||
label="이메일 주소*"
|
||||
type="text"
|
||||
placeholder="이메일 주소를 입력해주세요."
|
||||
value={email}
|
||||
onChange={onEmailChangeHandler}
|
||||
error={isemailError}
|
||||
message={emailErrorMessage}
|
||||
onKeyDown={onEmailKeyDownHandler}
|
||||
/>
|
||||
<InputBox
|
||||
ref={passwordRef}
|
||||
label="비밀번호*"
|
||||
type={passwordType}
|
||||
placeholder="비밀번호를 입력해주세요."
|
||||
value={password}
|
||||
onChange={onPasswordChangeHandler}
|
||||
error={isPasswordError}
|
||||
message={passwordErrorMessage}
|
||||
icon={passwordButtonIcon}
|
||||
onButtonClick={onPasswordButtonClickHandler}
|
||||
onKeyDown={onPasswordKeyDownHandler}
|
||||
/>
|
||||
<InputBox
|
||||
ref={passCheckRef}
|
||||
label="비밀번호 확인*"
|
||||
type={passCheckType}
|
||||
placeholder="비밀번호를 다시 입력해주세요."
|
||||
value={passCheck}
|
||||
onChange={onPassCheckChangeHandler}
|
||||
error={isPassCheckError}
|
||||
message={passCheckErrorMessage}
|
||||
icon={passCheckButtonIcon}
|
||||
onButtonClick={onPassCheckButtonClickHandler}
|
||||
onKeyDown={onPassCheckKeyDownHandler}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{page === 2 && (
|
||||
<>
|
||||
<InputBox
|
||||
ref={nicknameRef}
|
||||
label="닉네임*"
|
||||
type="text"
|
||||
placeholder="닉네임을 입력해주세요."
|
||||
value={nickname}
|
||||
onChange={onNicknameChangeHandler}
|
||||
error={isNicknameError}
|
||||
message={nicknameErrorMessage}
|
||||
onKeyDown={onNicknameKeyDownHandler}
|
||||
/>
|
||||
<InputBox
|
||||
ref={telNumberRef}
|
||||
label="휴대폰 번호*"
|
||||
type="text"
|
||||
placeholder="휴대폰 번호를 입력해주세요."
|
||||
value={telNumber}
|
||||
onChange={onTelNumberChangeHandler}
|
||||
error={isTelNumberError}
|
||||
message={telNumberErrorMessage}
|
||||
onKeyDown={onTelNumberKeyDownHandler}
|
||||
/>
|
||||
<InputBox
|
||||
ref={addressRef}
|
||||
label="주소*"
|
||||
type="text"
|
||||
placeholder="주소 찾기"
|
||||
value={address}
|
||||
onChange={onAddressChangeHandler}
|
||||
error={isAddressError}
|
||||
message={addressErrorMessage}
|
||||
icon="expand-right-light-icon"
|
||||
onButtonClick={onAddressButtonClickHandler}
|
||||
onKeyDown={onAddressKeyDownHandler}
|
||||
/>
|
||||
<InputBox
|
||||
ref={addressDetailRef}
|
||||
label="상세 주소*"
|
||||
type="text"
|
||||
placeholder="상세 주소를 입력해주세요."
|
||||
value={addressDetail}
|
||||
onChange={onAddressDetailChangeHandler}
|
||||
error={false}
|
||||
onKeyDown={onAddressDetailKeyDownHandler}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="employee-card-bottom">
|
||||
{page === 1 && (
|
||||
<div
|
||||
className="black-large-full-button"
|
||||
onClick={onNextButtonClickHandler}
|
||||
>
|
||||
{"다음단계"}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{page === 2 && (
|
||||
<>
|
||||
<div className="employee-consent-box">
|
||||
<div
|
||||
className="employee-check-box"
|
||||
onClick={onAgreedPersonalClickHandler}
|
||||
>
|
||||
<div
|
||||
className={`icon ${
|
||||
agreedPersonal
|
||||
? "check-round-fill-icon"
|
||||
: "check-ring-light-icon"
|
||||
}`}
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
isAgreedPersonalError
|
||||
? "employee-consent-title-error"
|
||||
: "employee-consent-title"
|
||||
}
|
||||
>
|
||||
{"개인정보동의"}
|
||||
</div>
|
||||
<div className="employee-consent-link">{"더보기 >"}</div>
|
||||
</div>
|
||||
<div
|
||||
className="black-large-full-button"
|
||||
onClick={onSignUpButtonClickHandler}
|
||||
>
|
||||
{"사용자 생성"}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// render: 인증화면 컴포넌트 렌더링 //
|
||||
return (
|
||||
<div id="employee-wrapper">
|
||||
<div className="employee-container">
|
||||
<div className="employee-jumbotron-box">
|
||||
<div className="employee-jumbotron-contents">
|
||||
<div className="employee-logo-icon"></div>
|
||||
<div className="employee-jumbotron-text-box">
|
||||
<div className="employee-jumbotron-text">{"환영합니다"}</div>
|
||||
<div className="employee-jumbotron-text">
|
||||
{"사용자 생성 page"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SignUpCard />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
170
board-front/src/views/Menu/employee/style.css
Normal file
@ -0,0 +1,170 @@
|
||||
#employee-wrapper {
|
||||
padding: 100px 0px 150px;
|
||||
min-width: 1440px;
|
||||
height: calc(100vh - 68px - 100px - 150px);
|
||||
min-height: 960px; /* 이거 없애면 카드 짜리몽땅하게 나옴 */
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
background-image: url(assets/auth-background.png);
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: auto;
|
||||
}
|
||||
|
||||
.employee-container {
|
||||
width: 1200px;
|
||||
height: 100%;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 7fr 5fr;
|
||||
column-gap: 78px;
|
||||
}
|
||||
|
||||
.employee-jumbotron-box {
|
||||
grid-column: 1 / 2;
|
||||
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.employee-jumbotron-contents {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.employee-logo-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
||||
background-image: url(assets/auth-logo.png);
|
||||
background-position: 50% 50%;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.employee-jumbotron-text-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.employee-jumbotron-text {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
|
||||
font-family: "GimhaeGaya";
|
||||
font-size: 40px;
|
||||
font-weight: 400;
|
||||
line-height: 140%;
|
||||
letter-spacing: -1.2px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.employee-card {
|
||||
grid-column: 2 / 3;
|
||||
border-radius: 10px;
|
||||
padding: 50px 50px 30px;
|
||||
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
box-shadow: 0px 4px 47px 0px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.employee-card-box {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.employee-card-top {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.employee-card-title-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.employee-card-title {
|
||||
color: rgba(0, 0, 0, 1);
|
||||
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.employee-card-page {
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.employee-card-bottom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.employee-description-box {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.employee-description {
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.employee-description-link {
|
||||
color: rgba(0, 0, 0, 1);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.employee-consent-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.employee-check-box {
|
||||
cursor: pointer;
|
||||
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.employee-consent-title {
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.employee-consent-title-error {
|
||||
color: rgba(255, 0, 0, 0.7);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.employee-consent-link {
|
||||
color: rgba(0, 0, 0, 1);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 140%;
|
||||
}
|
@ -4,8 +4,8 @@
|
||||
"settings": {
|
||||
"width": 2000,
|
||||
"height": 2000,
|
||||
"scrollTop": -647.182,
|
||||
"scrollLeft": -201.0012,
|
||||
"scrollTop": -561.1241,
|
||||
"scrollLeft": -344.4682,
|
||||
"zoomLevel": 0.8,
|
||||
"show": 431,
|
||||
"database": 4,
|
||||
|
225
pro.vuerd.json
@ -4,9 +4,9 @@
|
||||
"settings": {
|
||||
"width": 2000,
|
||||
"height": 2000,
|
||||
"scrollTop": -450.7084,
|
||||
"scrollLeft": -220.8091,
|
||||
"zoomLevel": 0.8,
|
||||
"scrollTop": -482.7981,
|
||||
"scrollLeft": -512.5388,
|
||||
"zoomLevel": 0.7,
|
||||
"show": 431,
|
||||
"database": 4,
|
||||
"databaseName": "",
|
||||
@ -92,7 +92,7 @@
|
||||
"color": ""
|
||||
},
|
||||
"meta": {
|
||||
"updateAt": 1726128309470,
|
||||
"updateAt": 1726716472171,
|
||||
"createAt": 1725003298900
|
||||
}
|
||||
},
|
||||
@ -179,31 +179,6 @@
|
||||
"createAt": 1725003329099
|
||||
}
|
||||
},
|
||||
"M44_l38-vVxHyOsq8CRPj": {
|
||||
"id": "M44_l38-vVxHyOsq8CRPj",
|
||||
"name": "auth",
|
||||
"comment": "권한 종류 테이블",
|
||||
"columnIds": [
|
||||
"6w-poCexUwKTv11dm80DX",
|
||||
"SE2wqh_QeOaWncRIlOWvL"
|
||||
],
|
||||
"seqColumnIds": [
|
||||
"6w-poCexUwKTv11dm80DX",
|
||||
"SE2wqh_QeOaWncRIlOWvL"
|
||||
],
|
||||
"ui": {
|
||||
"x": 664.5354,
|
||||
"y": 791.1966,
|
||||
"zIndex": 310,
|
||||
"widthName": 60,
|
||||
"widthComment": 93,
|
||||
"color": ""
|
||||
},
|
||||
"meta": {
|
||||
"updateAt": 1726031760442,
|
||||
"createAt": 1726031519594
|
||||
}
|
||||
},
|
||||
"y-7FD40j93P014zpeKbvD": {
|
||||
"id": "y-7FD40j93P014zpeKbvD",
|
||||
"name": "TB_file",
|
||||
@ -215,7 +190,8 @@
|
||||
"0QpiUcDf1av9QLfmjCltW",
|
||||
"CjgzCf_O-x7EEcNTEkH7W",
|
||||
"2m0cUujOxYkN1lY9vJdZy",
|
||||
"Pu329uwVq-ByQGbK3KvJz"
|
||||
"Pu329uwVq-ByQGbK3KvJz",
|
||||
"PZ-xcjVURB_3gUVln0zKe"
|
||||
],
|
||||
"seqColumnIds": [
|
||||
"AjF0zhA7k17RPuLP9x7NC",
|
||||
@ -228,7 +204,8 @@
|
||||
"HheFQhoJLpiAP99roG36N",
|
||||
"d94or2DRf2mIUNC3ZLfWV",
|
||||
"akr_pWs8iohI_f5bRRqzw",
|
||||
"T9PlNjJKiRkcy_8ItTwyE"
|
||||
"T9PlNjJKiRkcy_8ItTwyE",
|
||||
"PZ-xcjVURB_3gUVln0zKe"
|
||||
],
|
||||
"ui": {
|
||||
"x": 849.8114,
|
||||
@ -239,7 +216,7 @@
|
||||
"color": ""
|
||||
},
|
||||
"meta": {
|
||||
"updateAt": 1726128306215,
|
||||
"updateAt": 1726716553141,
|
||||
"createAt": 1726032771479
|
||||
}
|
||||
},
|
||||
@ -294,15 +271,15 @@
|
||||
"4EGixbjC44Sw0DlyDgRyc"
|
||||
],
|
||||
"ui": {
|
||||
"x": 1038.2139,
|
||||
"y": 142.3953,
|
||||
"x": 1139.4639,
|
||||
"y": 136.1453,
|
||||
"zIndex": 746,
|
||||
"widthName": 60,
|
||||
"widthComment": 120,
|
||||
"color": ""
|
||||
},
|
||||
"meta": {
|
||||
"updateAt": 1726128309471,
|
||||
"updateAt": 1726715523729,
|
||||
"createAt": 1726125593357
|
||||
}
|
||||
},
|
||||
@ -998,46 +975,6 @@
|
||||
"createAt": 1726031474345
|
||||
}
|
||||
},
|
||||
"6w-poCexUwKTv11dm80DX": {
|
||||
"id": "6w-poCexUwKTv11dm80DX",
|
||||
"tableId": "M44_l38-vVxHyOsq8CRPj",
|
||||
"name": "auth_user",
|
||||
"comment": "",
|
||||
"dataType": "",
|
||||
"default": "",
|
||||
"options": 0,
|
||||
"ui": {
|
||||
"keys": 0,
|
||||
"widthName": 60,
|
||||
"widthComment": 60,
|
||||
"widthDataType": 60,
|
||||
"widthDefault": 60
|
||||
},
|
||||
"meta": {
|
||||
"updateAt": 1726031724750,
|
||||
"createAt": 1726031632128
|
||||
}
|
||||
},
|
||||
"SE2wqh_QeOaWncRIlOWvL": {
|
||||
"id": "SE2wqh_QeOaWncRIlOWvL",
|
||||
"tableId": "M44_l38-vVxHyOsq8CRPj",
|
||||
"name": "auth_admin",
|
||||
"comment": "",
|
||||
"dataType": "",
|
||||
"default": "",
|
||||
"options": 0,
|
||||
"ui": {
|
||||
"keys": 0,
|
||||
"widthName": 64,
|
||||
"widthComment": 60,
|
||||
"widthDataType": 60,
|
||||
"widthDefault": 60
|
||||
},
|
||||
"meta": {
|
||||
"updateAt": 1726031730704,
|
||||
"createAt": 1726031632674
|
||||
}
|
||||
},
|
||||
"3zE6e4pkAmYsMkWFXR7Se": {
|
||||
"id": "3zE6e4pkAmYsMkWFXR7Se",
|
||||
"tableId": "Qf7oberqyXg9rwB2NXnFB",
|
||||
@ -1142,19 +1079,19 @@
|
||||
"id": "0QpiUcDf1av9QLfmjCltW",
|
||||
"tableId": "y-7FD40j93P014zpeKbvD",
|
||||
"name": "file_name",
|
||||
"comment": "파일명",
|
||||
"comment": "파일명+UID",
|
||||
"dataType": "VARCHAR2(50)",
|
||||
"default": "",
|
||||
"options": 8,
|
||||
"ui": {
|
||||
"keys": 0,
|
||||
"widthName": 60,
|
||||
"widthComment": 60,
|
||||
"widthComment": 66,
|
||||
"widthDataType": 81,
|
||||
"widthDefault": 60
|
||||
},
|
||||
"meta": {
|
||||
"updateAt": 1726034697376,
|
||||
"updateAt": 1726716592297,
|
||||
"createAt": 1726032839926
|
||||
}
|
||||
},
|
||||
@ -1697,6 +1634,26 @@
|
||||
"updateAt": 1726126986235,
|
||||
"createAt": 1726126948524
|
||||
}
|
||||
},
|
||||
"PZ-xcjVURB_3gUVln0zKe": {
|
||||
"id": "PZ-xcjVURB_3gUVln0zKe",
|
||||
"tableId": "y-7FD40j93P014zpeKbvD",
|
||||
"name": "originname",
|
||||
"comment": "파일명",
|
||||
"dataType": "VARCHAR2(20)",
|
||||
"default": "",
|
||||
"options": 8,
|
||||
"ui": {
|
||||
"keys": 0,
|
||||
"widthName": 62,
|
||||
"widthComment": 60,
|
||||
"widthDataType": 81,
|
||||
"widthDefault": 60
|
||||
},
|
||||
"meta": {
|
||||
"updateAt": 1726716614673,
|
||||
"createAt": 1726716553141
|
||||
}
|
||||
}
|
||||
},
|
||||
"relationshipEntities": {
|
||||
@ -1728,34 +1685,6 @@
|
||||
"createAt": 1726030193552
|
||||
}
|
||||
},
|
||||
"-ARsCEo_NPzq07sSq5MQA": {
|
||||
"id": "-ARsCEo_NPzq07sSq5MQA",
|
||||
"identification": false,
|
||||
"relationshipType": 8,
|
||||
"startRelationshipType": 2,
|
||||
"start": {
|
||||
"tableId": "5jPYxY_W7XcoLCO--IWtf",
|
||||
"columnIds": [
|
||||
"0DI-UPqbdhGb3Cl2tFA8O"
|
||||
],
|
||||
"x": 513.5517,
|
||||
"y": 640.1429,
|
||||
"direction": 2
|
||||
},
|
||||
"end": {
|
||||
"tableId": "Qf7oberqyXg9rwB2NXnFB",
|
||||
"columnIds": [
|
||||
"LttbW8cGsGrNKZzpmCS-e"
|
||||
],
|
||||
"x": 671.7857,
|
||||
"y": 624.3135,
|
||||
"direction": 1
|
||||
},
|
||||
"meta": {
|
||||
"updateAt": 1726030385147,
|
||||
"createAt": 1726030385147
|
||||
}
|
||||
},
|
||||
"03Bz1xU0ntcL6sONlZ7bS": {
|
||||
"id": "03Bz1xU0ntcL6sONlZ7bS",
|
||||
"identification": false,
|
||||
@ -1804,7 +1733,7 @@
|
||||
"DlV0i_mi378ktS7TX2MHG"
|
||||
],
|
||||
"x": 849.8114,
|
||||
"y": 818.5025,
|
||||
"y": 830.5025,
|
||||
"direction": 1
|
||||
},
|
||||
"meta": {
|
||||
@ -1812,34 +1741,6 @@
|
||||
"createAt": 1726032969804
|
||||
}
|
||||
},
|
||||
"4l4Tl9-Jkn6-9RofH8Ko2": {
|
||||
"id": "4l4Tl9-Jkn6-9RofH8Ko2",
|
||||
"identification": false,
|
||||
"relationshipType": 16,
|
||||
"startRelationshipType": 2,
|
||||
"start": {
|
||||
"tableId": "5jPYxY_W7XcoLCO--IWtf",
|
||||
"columnIds": [
|
||||
"0DI-UPqbdhGb3Cl2tFA8O"
|
||||
],
|
||||
"x": 986.8852,
|
||||
"y": 444.2819,
|
||||
"direction": 4
|
||||
},
|
||||
"end": {
|
||||
"tableId": "7E1EZCsr9O350-mnaBiSH",
|
||||
"columnIds": [
|
||||
"rufc5sONjStlKSZCvgemx"
|
||||
],
|
||||
"x": 803.3566000000001,
|
||||
"y": 281.9072,
|
||||
"direction": 2
|
||||
},
|
||||
"meta": {
|
||||
"updateAt": 1726125677582,
|
||||
"createAt": 1726125677582
|
||||
}
|
||||
},
|
||||
"SkzOTC78rK8MhfG26apJu": {
|
||||
"id": "SkzOTC78rK8MhfG26apJu",
|
||||
"identification": false,
|
||||
@ -1887,8 +1788,8 @@
|
||||
"columnIds": [
|
||||
"ikgt-Be_4Vn5KxSnjf-y_"
|
||||
],
|
||||
"x": 1038.2139,
|
||||
"y": 230.3953,
|
||||
"x": 1139.4639,
|
||||
"y": 224.1453,
|
||||
"direction": 1
|
||||
},
|
||||
"meta": {
|
||||
@ -2297,12 +2198,6 @@
|
||||
"comment": 1726030385146
|
||||
}
|
||||
],
|
||||
"-ARsCEo_NPzq07sSq5MQA": [
|
||||
"relationshipEntities",
|
||||
1726030385146,
|
||||
1726031237785,
|
||||
{}
|
||||
],
|
||||
"a97XkxWjV6uRrdF-XOlgO": [
|
||||
"tableColumnEntities",
|
||||
1726031243537,
|
||||
@ -2329,31 +2224,6 @@
|
||||
"name": 1726031485949
|
||||
}
|
||||
],
|
||||
"M44_l38-vVxHyOsq8CRPj": [
|
||||
"tableEntities",
|
||||
1726031519593,
|
||||
1726031915918,
|
||||
{
|
||||
"name": 1726031620188,
|
||||
"comment": 1726031630427
|
||||
}
|
||||
],
|
||||
"6w-poCexUwKTv11dm80DX": [
|
||||
"tableColumnEntities",
|
||||
1726031632127,
|
||||
-1,
|
||||
{
|
||||
"name": 1726031724749
|
||||
}
|
||||
],
|
||||
"SE2wqh_QeOaWncRIlOWvL": [
|
||||
"tableColumnEntities",
|
||||
1726031632673,
|
||||
-1,
|
||||
{
|
||||
"name": 1726031730703
|
||||
}
|
||||
],
|
||||
"3zE6e4pkAmYsMkWFXR7Se": [
|
||||
"tableColumnEntities",
|
||||
1726031824143,
|
||||
@ -2418,7 +2288,7 @@
|
||||
"name": 1726032863465,
|
||||
"options(notNull)": 1726033018351,
|
||||
"dataType": 1726033127517,
|
||||
"comment": 1726034697376
|
||||
"comment": 1726716592296
|
||||
}
|
||||
],
|
||||
"CjgzCf_O-x7EEcNTEkH7W": [
|
||||
@ -2537,12 +2407,6 @@
|
||||
"comment": 1726125677577
|
||||
}
|
||||
],
|
||||
"4l4Tl9-Jkn6-9RofH8Ko2": [
|
||||
"relationshipEntities",
|
||||
1726125677577,
|
||||
1726125687089,
|
||||
{}
|
||||
],
|
||||
"m55Qv1teASnVRvDMAXfc7": [
|
||||
"tableColumnEntities",
|
||||
1726125691894,
|
||||
@ -2750,6 +2614,17 @@
|
||||
"dataType": 1726126978821,
|
||||
"comment": 1726126986235
|
||||
}
|
||||
],
|
||||
"PZ-xcjVURB_3gUVln0zKe": [
|
||||
"tableColumnEntities",
|
||||
1726716553139,
|
||||
-1,
|
||||
{
|
||||
"name": 1726716559843,
|
||||
"dataType": 1726716603538,
|
||||
"comment": 1726716612601,
|
||||
"options(notNull)": 1726716614673
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|