-
Notifications
You must be signed in to change notification settings - Fork 1
Feat/adminpage #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
dasosann
wants to merge
2
commits into
main
Choose a base branch
from
feat/adminpage
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Feat/adminpage #41
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| "use client"; | ||
|
|
||
| import React from "react"; | ||
|
|
||
| interface AdminDateSelectorProps { | ||
| selectedDate: string; | ||
| onSelectDate: (date: string) => void; | ||
| } | ||
|
|
||
| export const AdminDateSelector = ({ | ||
| selectedDate, | ||
| onSelectDate, | ||
| }: AdminDateSelectorProps) => { | ||
| const dates = ["오늘", "내일", "모레"]; | ||
|
|
||
| return ( | ||
| <div className="flex w-full gap-2"> | ||
| {dates.map((date) => ( | ||
| <button | ||
| key={date} | ||
| onClick={() => onSelectDate(date)} | ||
| className={`h-12 flex-1 rounded-lg text-xl font-bold shadow-sm transition-all ${selectedDate === date ? "bg-[#ff775e] text-white" : "bg-[#b3b3b3] text-white"}`} | ||
| > | ||
| {date} | ||
| </button> | ||
| ))} | ||
| </div> | ||
| ); | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| "use client"; | ||
|
|
||
| import React, { useState, useRef, useEffect } from "react"; | ||
| import { ChevronDown } from "lucide-react"; | ||
|
|
||
| interface AdminDropdownProps { | ||
| options: string[]; | ||
| selectedValue: string; | ||
| onSelect: (value: string) => void; | ||
| height?: string; | ||
| className?: string; | ||
| } | ||
|
|
||
| export const AdminDropdown = ({ | ||
| options, | ||
| selectedValue, | ||
| onSelect, | ||
| height, | ||
| className | ||
| }: AdminDropdownProps) => { | ||
| const [isOpen, setIsOpen] = useState(false); | ||
| const dropdownRef = useRef<HTMLDivElement>(null); | ||
|
|
||
| useEffect(() => { | ||
| const handleClickOutside = (event: MouseEvent) => { | ||
| if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { | ||
| setIsOpen(false); | ||
| } | ||
| }; | ||
| document.addEventListener("mousedown", handleClickOutside); | ||
| return () => document.removeEventListener("mousedown", handleClickOutside); | ||
| }, []); | ||
|
|
||
| return ( | ||
| <div | ||
| ref={dropdownRef} | ||
| className={`relative w-full md:w-[136px] h-12 ${className || ""}`} | ||
| > | ||
| <div | ||
| onClick={() => setIsOpen(!isOpen)} | ||
| className="w-full h-full bg-[#f4f4f4] rounded-lg border border-[#e5e5e5] px-3 flex items-center justify-between cursor-pointer text-[18px] font-semibold text-black" | ||
| > | ||
| <span>{selectedValue}</span> | ||
| <ChevronDown | ||
| size={18} | ||
| className={`transition-transform duration-300 text-gray-400 ${isOpen ? "rotate-180" : ""}`} | ||
| /> | ||
| </div> | ||
|
|
||
| {isOpen && ( | ||
| <div | ||
| className="absolute top-full left-0 w-full bg-white border border-[#e5e5e5] mt-1 rounded-lg shadow-lg z-50 overflow-y-auto" | ||
| style={{ maxHeight: height || "200px" }} | ||
| > | ||
| {options.map((option) => ( | ||
| <div | ||
| key={option} | ||
| onClick={() => { | ||
| onSelect(option); | ||
| setIsOpen(false); | ||
| }} | ||
| className="px-3 py-2 hover:bg-[#f4f4f4] cursor-pointer text-[18px] font-medium text-black" | ||
| > | ||
| {option} | ||
| </div> | ||
| ))} | ||
| </div> | ||
| )} | ||
| </div> | ||
| ); | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| "use client"; | ||
|
|
||
| import React from "react"; | ||
| import Image from "next/image"; | ||
| import { useRouter } from "next/navigation"; | ||
| import { ChevronDown } from "lucide-react"; | ||
|
|
||
| interface AdminHeaderProps { | ||
| adminSelect?: string; | ||
| setAdminSelect?: (val: string) => void; | ||
| university?: string; | ||
| role?: string; | ||
| nickname?: string; | ||
| } | ||
|
|
||
| export const AdminHeader = ({ | ||
| adminSelect, | ||
| setAdminSelect, | ||
| university, | ||
| role, | ||
| nickname | ||
| }: AdminHeaderProps) => { | ||
| const router = useRouter(); | ||
|
|
||
| const goToMainButton = () => { | ||
| if (setAdminSelect) setAdminSelect("Main"); | ||
| router.push("/adminpage/myPage"); | ||
| }; | ||
|
|
||
| const goToTeamButton = () => { | ||
| if (setAdminSelect) setAdminSelect("팀관리"); | ||
| router.push("/adminpage/myPage?tab=팀관리"); | ||
| }; | ||
|
|
||
| const goToMemberButton = () => { | ||
| if (setAdminSelect) setAdminSelect("가입자관리"); | ||
| router.push("/adminpage/myPage?tab=가입자관리"); | ||
| }; | ||
|
|
||
| const getRoleLabel = (role?: string) => { | ||
| if (!role) return ""; | ||
| return role.includes("ADMIN") ? "관리자" : "오퍼레이터"; | ||
| }; | ||
|
|
||
| return ( | ||
| <header className="w-full flex h-[88px] font-sans justify-between items-center px-12 bg-white border-b border-[#cdcdcd] sticky top-0 z-[19999]"> | ||
| <Image | ||
| src="/logo/admin_header_logo.svg" | ||
| alt="코매칭 로고" | ||
| width={140} | ||
| height={40} | ||
| className="cursor-pointer" | ||
| onClick={() => router.push("/adminpage")} | ||
| /> | ||
|
|
||
| <nav className="flex justify-center items-center gap-[4.5em] whitespace-nowrap h-full"> | ||
| <div | ||
| onClick={goToMainButton} | ||
| className={`px-6 py-[29.5px] text-2xl font-semibold cursor-pointer h-full flex items-center transition-all ${adminSelect === "Main" ? "border-b-4 border-black text-black" : "text-[#808080]"}`} | ||
| > | ||
| Main | ||
| </div> | ||
|
Comment on lines
+57
to
+62
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 시맨틱 HTML과 Next.js의 성능 최적화 기능을 활용하기 위해 References
|
||
|
|
||
| <div | ||
| onClick={goToMemberButton} | ||
| className={`px-2 py-[29.5px] text-2xl font-semibold cursor-pointer h-full flex items-center transition-all ${adminSelect === "가입자관리" ? "border-b-4 border-black text-black" : "text-[#808080]"}`} | ||
| > | ||
| 가입자관리 | ||
| </div> | ||
|
|
||
| <div | ||
| onClick={goToTeamButton} | ||
| className={`px-[10px] py-[29.5px] flex items-center gap-2 cursor-pointer h-full transition-all ${adminSelect === "팀관리" ? "border-b-4 border-black text-black" : "text-[#808080]"}`} | ||
| > | ||
| <span className="text-2xl font-semibold">팀 관리</span> | ||
| <div className="rounded-full bg-[#ff775e] text-[10px] font-bold text-white w-6 h-6 flex justify-center items-center"> | ||
| 3 | ||
| </div> | ||
| </div> | ||
| </nav> | ||
|
|
||
| <div className="p-2 h-[50px] bg-[#f3f3f3] text-black whitespace-nowrap flex justify-center items-center text-right gap-4 rounded-lg"> | ||
| <div className="flex flex-col font-medium leading-tight"> | ||
| <div className="text-[#808080] text-xs font-semibold">{university}</div> | ||
| <div className="text-sm">{getRoleLabel(role)} {nickname}님</div> | ||
| </div> | ||
| <ChevronDown size={16} className="text-gray-400 shrink-0" /> | ||
| </div> | ||
| </header> | ||
| ); | ||
| }; | ||
|
|
||
| export const AdminRegisterHeader = () => { | ||
| const router = useRouter(); | ||
| return ( | ||
| <header className="w-full flex h-[88px] font-sans justify-between items-center px-12 bg-white border-b border-[#cdcdcd]"> | ||
| <Image | ||
| src="/logo/admin_header_logo.svg" | ||
| alt="코매칭 로고" | ||
| width={140} | ||
| height={40} | ||
| className="cursor-pointer" | ||
| onClick={() => router.push("/adminpage")} | ||
| /> | ||
| </header> | ||
| ); | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| "use client"; | ||
|
|
||
| import React from "react"; | ||
|
|
||
| interface AdminListItemProps { | ||
| title: string; | ||
| subTitle?: string; | ||
| statusText: string; | ||
| date: string; | ||
| startTime: string; | ||
| endTime: string; | ||
| onCancel?: () => void; | ||
| cancelButtonText?: string; | ||
| } | ||
|
|
||
| export const AdminListItem = ({ | ||
| title, | ||
| subTitle, | ||
| statusText, | ||
| date, | ||
| startTime, | ||
| endTime, | ||
| onCancel, | ||
| cancelButtonText | ||
| }: AdminListItemProps) => { | ||
| return ( | ||
| <div className="w-full border-b border-[#808080] py-6 flex flex-col gap-2"> | ||
| <div className="flex gap-8 items-center"> | ||
| <span className="text-2xl font-medium text-[#828282] shrink-0">{statusText}</span> | ||
| <span className="text-2xl font-semibold text-[#1a1a1a] truncate">{title} {subTitle && <span className="text-gray-500 font-normal ml-2">{subTitle}</span>}</span> | ||
| </div> | ||
| <div className="flex flex-wrap items-center justify-between gap-4 mt-2"> | ||
| <div className="flex flex-wrap gap-8 text-2xl font-semibold text-black"> | ||
| <div className="flex gap-4"> | ||
| <span className="text-[#808080] font-medium w-[137px]">시작일:</span> | ||
| <span className="text-[#4d4d4d] font-medium min-w-[120px]">{date}</span> | ||
| </div> | ||
| <div className="flex gap-4"> | ||
| <span className="text-[#808080] font-medium w-[96px]">시작 시각:</span> | ||
| <span className="text-[#4d4d4d] font-medium min-w-[80px]">{startTime}</span> | ||
| </div> | ||
| <div className="flex gap-4"> | ||
| <span className="text-[#808080] font-medium w-[107px]">종료 시각:</span> | ||
| <span className="text-[#4d4d4d] font-medium min-w-[80px]">{endTime}</span> | ||
| </div> | ||
| </div> | ||
|
|
||
| {onCancel && ( | ||
| <button | ||
| onClick={onCancel} | ||
| className="w-[120px] h-12 bg-[#dd272a] text-white text-xl font-bold rounded-lg shadow-md hover:bg-red-700 transition-colors" | ||
| > | ||
| {cancelButtonText || "취소"} | ||
| </button> | ||
| )} | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| "use client"; | ||
|
|
||
| import React from "react"; | ||
| import { Ban } from "lucide-react"; | ||
|
|
||
| export default function AdminNotAllowed() { | ||
| return ( | ||
| <div className="flex w-full flex-col items-center pt-[200px] font-sans"> | ||
| <Ban size={100} className="text-[#858585]" /> | ||
| <div className="mt-4 text-center text-[36px] font-bold text-[#4d4d4d]"> | ||
| 미승인 오퍼레이터입니다. | ||
| <br /> | ||
| 관리자의 승인을 대기해 주세요. | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| "use client"; | ||
|
|
||
| import React from "react"; | ||
| import { AdminDropdown } from "./AdminDropdown"; | ||
|
|
||
| interface AdminTimeRowProps { | ||
| hours: string[]; | ||
| minutes: string[]; | ||
| selectedHour: string; | ||
| selectedMinute: string; | ||
| onHourSelect: (hour: string) => void; | ||
| onMinuteSelect: (minute: string) => void; | ||
| suffix: string; | ||
| } | ||
|
|
||
| export const AdminTimeRow = ({ | ||
| hours, | ||
| minutes, | ||
| selectedHour, | ||
| selectedMinute, | ||
| onHourSelect, | ||
| onMinuteSelect, | ||
| suffix, | ||
| }: AdminTimeRowProps) => { | ||
| return ( | ||
| <div className="flex flex-wrap items-center gap-4"> | ||
| <AdminDropdown | ||
| options={hours} | ||
| selectedValue={selectedHour} | ||
| onSelect={onHourSelect} | ||
| /> | ||
| <span className="text-2xl font-semibold">시</span> | ||
| <AdminDropdown | ||
| options={minutes} | ||
| selectedValue={selectedMinute} | ||
| onSelect={onMinuteSelect} | ||
| /> | ||
| <span className="text-2xl font-semibold">{suffix}</span> | ||
| </div> | ||
| ); | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| "use client"; | ||
|
|
||
| import React from "react"; | ||
|
|
||
| interface AdminWarnItemProps { | ||
| reason: string; | ||
| time: string; | ||
| } | ||
|
|
||
| export const AdminWarnItem = ({ reason, time }: AdminWarnItemProps) => { | ||
| return ( | ||
| <div className="w-full h-[95px] flex items-center gap-4 border-b border-[#808080]"> | ||
| <div className="w-[160px] text-2xl font-semibold text-black text-center shrink-0"> | ||
| {reason} | ||
| </div> | ||
| <div className="text-2xl font-medium text-[#828282]"> | ||
| {time} | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| "use client"; | ||
|
|
||
| import React from "react"; | ||
| import { TriangleAlert } from "lucide-react"; | ||
|
|
||
| interface AdminWarningModalProps { | ||
| isOpen: boolean; | ||
| onClose: () => void; | ||
| message: React.ReactNode; | ||
| } | ||
|
|
||
| export const AdminWarningModal = ({ | ||
| isOpen, | ||
| onClose, | ||
| message, | ||
| }: AdminWarningModalProps) => { | ||
| if (!isOpen) return null; | ||
|
|
||
| return ( | ||
| <div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/50 p-4"> | ||
| <div className="flex w-full max-w-[423px] flex-col items-center rounded-[24px] bg-white shadow-2xl"> | ||
| <div className="flex flex-col items-center gap-4 p-10 text-center"> | ||
| <TriangleAlert size={60} className="text-[#ff775e]" /> | ||
| <div className="text-2xl leading-relaxed font-semibold text-black"> | ||
| {message} | ||
| </div> | ||
| </div> | ||
| <div | ||
| className="flex h-14 w-full cursor-pointer items-center justify-center rounded-b-[24px] border-t border-[#b3b3b3] text-xl font-bold text-[#ff775e] hover:bg-gray-50" | ||
| onClick={onClose} | ||
| > | ||
| 확인 | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
웹 접근성을 위해 클릭 가능한
div대신<button>요소를 사용하는 것이 좋습니다.type="button"을 명시하고,aria-haspopup="listbox"와aria-expanded={isOpen}속성을 추가하면 스크린 리더가 이 요소의 역할과 상태를 정확히 인지할 수 있습니다.References