Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/api/admin/customerService/inquiry.api.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import type { ApiCommonBasicType } from '../../../models/apiCommon';
import type {
AdminInquiryChangeSearchParams,
ApiAdminInquiry,
ApiAdminInquiryDetail,
InquiryAnswerBody,
} from '../../../models/inquiry';
import { httpClient } from '../../http.api';

export const getAllInquiries = async () => {
export const getAllInquiries = async (
childSearchParams: AdminInquiryChangeSearchParams
) => {
try {
const response = await httpClient.get<ApiAdminInquiry>(`/inquiry`);
const response = await httpClient.get<ApiAdminInquiry>(`/inquiry`, {
params: childSearchParams,
});
return response.data.data;
} catch (e) {
console.error('전체 문의 조회 에러', e);
Expand Down
10 changes: 10 additions & 0 deletions src/api/mypage.api.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ApiCommonBasicType } from '../models/apiCommon';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type

import type {
ApiUserInfo,
ApiUserInfoImg,
Expand Down Expand Up @@ -53,6 +54,15 @@ export const patchMyProfileImg = async (file: File) => {
}
};

export const patchGithubLink = async (githubUrl: string) => {
try {
await httpClient.patch<ApiCommonBasicType>('/user/github', { githubUrl });
} catch (error) {
console.error('프로필 깃허브 업데이트: ', error);
throw error;
}
};

export const getMyJoinedProjectList = async () => {
try {
const response = await httpClient.get<ApiJoinedProject>(
Expand Down
28 changes: 27 additions & 1 deletion src/components/admin/adminInquiry/AdminInquiry.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,33 @@ export const AdminInquiryCategory = styled.span`
font-weight: 500;
`;

export const AdminInquiryUser = styled.div``;
export const AdminInquiryUserWrapper = styled.div`
position: relative;
`;

export const AdminInquiryUser = styled.button`
width: fit-content;
font-size: 1.1rem;
z-index: 1;
transition: color 0.1s ease-in-out;
&:hover {
color: ${({ theme }) => theme.color.lightnavy};
}
`;

export const AdminInquiryUserCheckDropdown = styled.div`
position: absolute;
top: 0.8rem;
right: 0;
background: ${({ theme }) => theme.color.white};
border-radius: ${({ theme }) => theme.borderRadius.primary};
border: 1px solid ${({ theme }) => theme.color.lightgrey};
padding: 0.3rem 0.5rem;
z-index: 100000;
box-shadow: 3px 2px 19px -6px rgba(0, 0, 0, 0.75);
-webkit-box-shadow: 3px 2px 19px -6px rgba(0, 0, 0, 0.75);
-moz-box-shadow: 3px 2px 19px -6px rgba(0, 0, 0, 0.75);
`;

export const AdminInquiryDate = styled.span`
font-size: 0.9rem;
Expand Down
24 changes: 23 additions & 1 deletion src/components/admin/adminInquiry/AdminInquiry.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useSearchParams } from 'react-router-dom';
import { ADMIN_ROUTE } from '../../../constants/routes';
import type { AdminInquiry as TAdminInquiry } from '../../../models/inquiry';
import ContentBorder from '../../common/contentBorder/ContentBorder';
Expand All @@ -8,12 +9,33 @@ interface AdminInquiryProps {
}

export default function AdminInquiry({ list }: AdminInquiryProps) {
const [searchParams, setSearchParams] = useSearchParams();

const handleClickLookupUser = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
e.preventDefault();

const id = String(list.user.id);
const userId = id || '';
const nickname = list.user.nickname;

const newParams = new URLSearchParams(searchParams);
newParams.set('userId', userId);
newParams.set('nickname', nickname);

setSearchParams(newParams);
};

return (
<S.AdminInquiryContainer to={`${ADMIN_ROUTE.detail}/${list.id}`}>
<S.AdminInquiryWrapper>
<S.AdminInquiryCategory>[{list.category}]</S.AdminInquiryCategory>
<S.AdminInquiryTitle>{list.title}</S.AdminInquiryTitle>
<S.AdminInquiryUser>{list.user.nickname}</S.AdminInquiryUser>
<S.AdminInquiryUserWrapper>
<S.AdminInquiryUser onClick={handleClickLookupUser}>
{list.user.nickname}
</S.AdminInquiryUser>
</S.AdminInquiryUserWrapper>
<S.AdminInquiryDate>{list.createdAt}</S.AdminInquiryDate>
<S.AdminInquiryState $hasAnswer={list.state}>
{list.state ? '답변완료' : '확인중'}
Expand Down
3 changes: 2 additions & 1 deletion src/components/admin/adminInquiry/AdminInquiryList.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export const SpinnerWrapper = styled(SpinnerWrapperStyled)``;
export const AdminInquiryListContainer = styled.section`
width: 100%;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
`;

export const AdminInquiryListWrapper = styled.div`
Expand Down
15 changes: 14 additions & 1 deletion src/components/admin/adminInquiry/AdminInquiryList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import { useSearchParams } from 'react-router-dom';
import { useGetAllInquiries } from '../../../hooks/admin/useGetAllInquiries';
import Spinner from '../../user/mypage/Spinner';
import AdminInquiry from './AdminInquiry';
import * as S from './AdminInquiryList.styled';
import AdminInquiryListLookup from './AdminInquiryListLookup';
import type { AdminInquiryChangeSearchParams } from '../../../models/inquiry';

export type SearchParamsInquiryKeyType = keyof AdminInquiryChangeSearchParams;
export default function AdminInquiryList() {
const { allInquiriesData, isLoading } = useGetAllInquiries();
const [searchParams, setSearchParams] = useSearchParams();
const userId = searchParams.get('userId') || '';
const startDate = searchParams.get('startDate') || '';
const endDate = searchParams.get('endDate') || '';
const { allInquiriesData, isLoading } = useGetAllInquiries({
userId,
startDate,
endDate,
});

if (isLoading) {
return (
Expand All @@ -18,6 +30,7 @@ export default function AdminInquiryList() {

return (
<S.AdminInquiryListContainer>
<AdminInquiryListLookup />
<S.AdminInquiryListWrapper>
{allInquiriesData.map((list) => (
<AdminInquiry key={list.id} list={list} />
Expand Down
67 changes: 67 additions & 0 deletions src/components/admin/adminInquiry/AdminInquiryListLookup.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import styled from 'styled-components';

export const LookupContainer = styled.nav`
width: 80%;
margin-bottom: 1rem;

input[type='date'] {
position: relative;
padding: 14px;
width: 150px;
height: 30px;
font-size: 14px;
color: ${({ theme }) => theme.color.placeholder};
border: none;
border-bottom: 1px solid ${({ theme }) => theme.color.grey};
}
input[type='date']::-webkit-calendar-picker-indicator {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: auto;
height: auto;
color: transparent;
background: transparent;
}
`;

export const LookupWrapper = styled.form`
display: flex;
justify-content: space-between;
`;

export const LookupUser = styled.input`
border-bottom: 1px solid ${({ theme }) => theme.color.placeholder};
width: fit-content;
`;

export const LookupDateWrapper = styled.div`
display: flex;
gap: 1rem;
`;

export const LookupStartDate = styled.input``;

export const LookupJoinDate = styled.span``;

export const LookupEndDate = styled.input``;

export const LookupIconWrapper = styled.div`
display: flex;
gap: 2rem;

svg {
width: 1.5rem;
height: 1.5rem;
}
`;

export const IconDefault = styled.button`
color: ${({ theme }) => theme.color.deepGrey};
`;

export const IconSearch = styled.button`
color: ${({ theme }) => theme.color.deepGrey};
`;
128 changes: 128 additions & 0 deletions src/components/admin/adminInquiry/AdminInquiryListLookup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React, { useEffect, useState } from 'react';
import type { SearchParamsInquiryKeyType } from './AdminInquiryList';
import * as S from './AdminInquiryListLookup.styled';
import { MagnifyingGlassIcon, XMarkIcon } from '@heroicons/react/24/outline';
import { useSearchParams } from 'react-router-dom';
import { AdminInquiryChangeSearchParams } from '../../../models/inquiry';
import Modal from '../../common/modal/Modal';
import { useModal } from '../../../hooks/useModal';
import { MODAL_MESSAGE } from '../../../constants/user/modalMessage';

export default function AdminInquiryListLookup() {
const [searchParams, setSearchParams] = useSearchParams();
const userId = searchParams.get('userId') || '';
const startDate = searchParams.get('startDate') || '';
const endDate = searchParams.get('endDate') || '';
const nickname = searchParams.get('nickname') || '';
const { isOpen, message, handleModalOpen, handleModalClose } = useModal();
const [searchFilters, setSearchFilters] = useState<
Omit<AdminInquiryChangeSearchParams, 'userId'>
>({
startDate,
endDate,
});

useEffect(() => {
const startDate = searchParams.get('startDate') || '';
const endDate = searchParams.get('endDate') || '';
setSearchFilters({
startDate,
endDate,
});
}, [searchParams, setSearchFilters]);

const handleSubmitChangeParams = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

const { startDate, endDate } = searchFilters;

const newParams = new URLSearchParams(searchParams);

if (startDate && !endDate) {
return handleModalOpen(MODAL_MESSAGE.endDateEmpty);
} else if (!startDate && endDate) {
return handleModalOpen(MODAL_MESSAGE.startDateEmpty);
} else if (startDate && endDate) {
newParams.set('startDate', startDate);
newParams.set('endDate', endDate);
} else {
newParams.delete('startDate');
newParams.delete('endDate');
}

setSearchParams(newParams);
};

const handleChangeDate = (
e: React.ChangeEvent<HTMLInputElement>,
key: SearchParamsInquiryKeyType
) => {
const value = e.currentTarget.value;

setSearchFilters((prev) => {
switch (key) {
case 'startDate': {
if (prev.endDate !== '' && prev.endDate < value) {
handleModalOpen(MODAL_MESSAGE.startDateInvalid);
return prev;
}
return { ...prev, startDate: value };
}
case 'endDate': {
if (prev.startDate !== '' && prev.startDate > value) {
handleModalOpen(MODAL_MESSAGE.endDateInvalid);
return prev;
}
return { ...prev, endDate: value };
}
default: {
return prev;
}
}
});
};

const handleClickInit = () => {
setSearchParams({});
setSearchFilters({ startDate: '', endDate: '' });
};

return (
<S.LookupContainer>
<S.LookupWrapper onSubmit={handleSubmitChangeParams}>
<S.LookupUser
type='text'
value={nickname}
placeholder='닉네임을 클릭하세요.'
readOnly
/>
<S.LookupDateWrapper>
<S.LookupStartDate
type='date'
value={searchFilters.startDate}
onChange={(e) => handleChangeDate(e, 'startDate')}
/>
<S.LookupJoinDate> ~ </S.LookupJoinDate>
<S.LookupEndDate
type='date'
value={searchFilters.endDate}
onChange={(e) => handleChangeDate(e, 'endDate')}
/>
</S.LookupDateWrapper>
<S.LookupIconWrapper>
{(userId || startDate || endDate) && (
<S.IconDefault type='button' onClick={handleClickInit}>
<XMarkIcon />
</S.IconDefault>
)}
<S.IconSearch type='submit'>
<MagnifyingGlassIcon />
</S.IconSearch>
</S.LookupIconWrapper>
</S.LookupWrapper>
<Modal isOpen={isOpen} onClose={handleModalClose}>
{message}
</Modal>
</S.LookupContainer>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@ import Avatar from '../../../common/avatar/Avatar';
import { ADMIN_ROUTE } from '../../../../constants/routes';
import arrow_right from '../../../../assets/ArrowRight.svg';
import Spinner from '../../../user/mypage/Spinner';
import { AdminInquiryChangeSearchParams } from '../../../../models/inquiry';

const InquiresPreview = () => {
const { allInquiriesData, isLoading, isFetching } = useGetAllInquiries();
const childSearchParams: AdminInquiryChangeSearchParams = {
userId: '',
startDate: '',
endDate: '',
};
const { allInquiriesData, isLoading, isFetching } =
useGetAllInquiries(childSearchParams);

if (isLoading || isFetching) {
return (
Expand Down
Loading