[cs] WIP: 알림톡발송이력관리 페이지
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
import { NtftalkHistoryPage } from "@/pages/ntftalk-history";
|
||||||
|
|
||||||
|
export default NtftalkHistoryPage;
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
# 화면설계서: 알림톡발송이력관리
|
||||||
|
|
||||||
|
## 1. 기본 정보
|
||||||
|
|
||||||
|
| 항목 | 내용 |
|
||||||
|
| ---------- | -------------------------------------------- |
|
||||||
|
| 버전 | 1.0 |
|
||||||
|
| 프로세스명 | UI설계 |
|
||||||
|
| 화면명 | 알림톡발송이력관리 |
|
||||||
|
| 화면 유형 | [x] 조회 [ ] 입력 [ ] 삭제 [ ] 수정 [ ] 출력 |
|
||||||
|
| 메뉴 경로 | home > 시스템관리 > 알림톡발송이력관리 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 화면 목적
|
||||||
|
|
||||||
|
알림톡 발송 이력을 **기간·내용·수신자·결과·시스템** 조건으로 검색하고, **발송 시스템·알림 내용·수신자·수신 시각·성공 여부**를 목록으로 확인한다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 화면 구성
|
||||||
|
|
||||||
|
### 3-1. 전체 레이아웃
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 페이지 제목: 알림톡발송이력관리 브레드크럼 (우측 상단) │
|
||||||
|
├────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ [영역 A] 검색 필터 │
|
||||||
|
│ · 조회기간, 알림내용, 사용자, 결과, 시스템 │
|
||||||
|
│ · [조회], [초기화] │
|
||||||
|
├────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ [영역 B] 알림톡 발송 이력 그리드 │
|
||||||
|
│ · 순번, 시스템, 알림내용, 수신자, 수신일, 결과 │
|
||||||
|
│ · 페이지네이션 │
|
||||||
|
└────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 영역 A: 검색 필터
|
||||||
|
|
||||||
|
### 4-1. 필드 정의
|
||||||
|
|
||||||
|
| 표시명 | 입력 유형 | 필수 | 기본값 | API 파라미터 매핑 | 비고 |
|
||||||
|
| -------- | -------------------- | ---- | ------------- | ----------------------------------- | ------------------------------------------- |
|
||||||
|
| 조회기간 | InputDateRangePicker | N | 1개월 전~오늘 | `dateFrom`, `dateTo` (`yyyy-MM-dd`) | `src/shared/ui/input-date-range-picker.tsx` |
|
||||||
|
| 알림내용 | 텍스트 | N | 공백 | `content` | |
|
||||||
|
| 사용자 | 텍스트 | N | 공백 | `recipientNum` | |
|
||||||
|
| 결과 | 드롭다운 | N | 전체 | `successYn` | `SUCCESS` \| `FAIL` \| `ALL` |
|
||||||
|
| 시스템 | SiteSelect | N | 전체 | `system` | `src/entities/site/ui/site-select.tsx` |
|
||||||
|
|
||||||
|
### 4-2. 버튼
|
||||||
|
|
||||||
|
| 버튼명 | 동작 |
|
||||||
|
| ------ | ---------------------------------------------------------------- |
|
||||||
|
| 조회 | 현재 필터로 영역 B 목록을 재조회한다. 페이지는 1페이지로 초기화. |
|
||||||
|
| 초기화 | 검색 필드를 기본값으로 되돌린다. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 영역 B: 알림톡 발송 이력 그리드
|
||||||
|
|
||||||
|
### 5-1. 그리드 컬럼
|
||||||
|
|
||||||
|
| 표시명 | Field | 비고 |
|
||||||
|
| -------- | -------------- | ------------------------------------------------------------------------- |
|
||||||
|
| 순번 | — | 클라이언트 표시용 인덱스(서버 필드 없음). 페이지네이션 시 오프셋 반영 |
|
||||||
|
| 시스템 | `siteSystem` | 응답 시 `templateCode` 앞 4자 기반 파생(행정지원·하수도통계·포털·기타) |
|
||||||
|
| 알림내용 | `content` | 긴 텍스트 말줄임 처리. 툴팁 적용하여 전체 텍스트 확인 가능. |
|
||||||
|
| 수신자 | `recipientNum` | 실데이터·마스킹 규칙 **TBD** |
|
||||||
|
| 수신일 | `dateRslt` | `YYYY-MM-DD HH:mm:ss` 형식 표시. |
|
||||||
|
| 결과 | `successYn` | UI 표시: `SUCCESS` → 성공, `FAIL` → 실패(백엔드는 `reportCode` 기반 파생) |
|
||||||
|
|
||||||
|
- 행 클릭 UX 없음.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. API/DTO 명세
|
||||||
|
|
||||||
|
### 6-1. 공통
|
||||||
|
|
||||||
|
- 목록 응답: Spring `Page` 직렬화 형태 — 프론트 타입 `PageableList<NtftalkMmtLogHistory>` (`content`, `totalElements`, `totalPages`, `number`, `size` 등).
|
||||||
|
- 목록 요청 쿼리: `Pageable` 표준 파라미터 `page`, `size`, `sort` (`PageableParams`).
|
||||||
|
|
||||||
|
### 6-2. 알림톡 발송 이력 API
|
||||||
|
|
||||||
|
Base: `/ntftalk-service/api/v1/ata/mmt-log-history`
|
||||||
|
|
||||||
|
| 기능 | Method | URL | Request DTO | Response DTO | API(클라이언트 함수명) |
|
||||||
|
| ----------------- | ------ | ----------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | ----------------------------- |
|
||||||
|
| 목록 조회(페이징) | GET | `/ata/mmt-log-history` | Query: `AtaMmtLogHistorySearchDto` 필드 + `Pageable`(`page`,`size`,`sort`) | `Page<AtaMmtLogHistoryResponseDto>` → `NtftalkMmtLogHistoryPageList` | `getNtftalkMmtLogHistoryPage` |
|
||||||
|
| 상세 조회 | GET | `/ata/mmt-log-history/{mtPr}` | Path: `mtPr` | `AtaMmtLogHistoryResponseDto` → `NtftalkMmtLogHistory` | `getNtftalkMmtLogHistory` |
|
||||||
|
| 등록 | POST | `/ata/mmt-log-history` | Body: `AtaMmtLogHistorySaveRequestDto` → `NtftalkMmtLogHistorySavePayload` | `AtaMmtLogHistoryResponseDto` | `saveNtftalkMmtLogHistory` |
|
||||||
|
| 수정 | PUT | `/ata/mmt-log-history/{mtPr}` | Path: `mtPr`, Body: `AtaMmtLogHistoryUpdateRequestDto` → `NtftalkMmtLogHistoryUpdatePayload` | `AtaMmtLogHistoryResponseDto` | `updateNtftalkMmtLogHistory` |
|
||||||
|
| 삭제 | DELETE | `/ata/mmt-log-history/{mtPr}` | Path: `mtPr` | 없음(204) | `deleteNtftalkMmtLogHistory` |
|
||||||
|
|
||||||
|
#### 검색 Query ↔ TypeScript (`NtftalkMmtLogHistorySearchParams`)
|
||||||
|
|
||||||
|
| Query (Java 필드) | TS 필드 | 설명 |
|
||||||
|
| ----------------- | --------------- | ------------------------------------ |
|
||||||
|
| `subject` | `subject?` | 메시지 제목 |
|
||||||
|
| `content` | `content?` | 전송 메시지 내용 |
|
||||||
|
| `recipientNum` | `recipientNum?` | 수신자 전화번호 |
|
||||||
|
| `msgStatus` | `msgStatus?` | `1` 전송대기, `2` 결과대기, `3` 완료 |
|
||||||
|
| `reportCode` | `reportCode?` | 전송 결과 코드(`1000`: 성공) |
|
||||||
|
| `dateFrom` | `dateFrom?` | `yyyy-MM-dd` |
|
||||||
|
| `dateTo` | `dateTo?` | `yyyy-MM-dd` |
|
||||||
|
| `system` | `system?` | `ADSS` \| `CWMS` \| `PORT` \| `ALL` |
|
||||||
|
| `successYn` | `successYn?` | `SUCCESS` \| `FAIL` \| `ALL` |
|
||||||
|
|
||||||
|
#### 응답 행 ↔ TypeScript (`NtftalkMmtLogHistory`)
|
||||||
|
|
||||||
|
백엔드 `AtaMmtLogHistoryResponseDto`와 동일 필드명: `mtPr`, `mtRefkey`, `priority`, `dateClientReq`, `subject`, `content`, `callback`, `msgStatus`, `recipientNum`, `dateMtSent`, `dateRslt`, `dateMtReport`, `reportCode`, `countryCode`, `msgType`, `regDate`, `senderKey`, `templateCode`, `siteSystem`, `successYn`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. UI 구현 가이드
|
||||||
|
|
||||||
|
### 7-1. 기술 스택·원칙
|
||||||
|
|
||||||
|
- **UI**: `@common/components` 공통 컴포넌트
|
||||||
|
- **테이블**: `@tanstack/react-table` + `useDataTable`(`src/shared/lib/use-data-table.tsx`)
|
||||||
|
- **데이터**: `@tanstack/react-query` — 목록은 `getNtftalkMmtLogHistoryPage`, 무한 스크롤 vs 페이지 UI는 **TBD**
|
||||||
|
- **레이아웃**: App Router `app/(with-base-layout)/ntftalk-history/page.tsx`에서 페이지 UI 마운트
|
||||||
|
|
||||||
|
### 7-2. 모듈·파일 배치
|
||||||
|
|
||||||
|
| 구분 | 경로 |
|
||||||
|
| --------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| 라우트 엔트리 | `apps/cs/app/(with-base-layout)/ntftalk-history/page.tsx` |
|
||||||
|
| 페이지 셸 | `apps/cs/src/pages/ntftalk-history/ui/ntftalk-history-page.tsx` |
|
||||||
|
| 컬럼 정의 | `apps/cs/src/pages/ntftalk-history/model/*-table-columns.tsx` |
|
||||||
|
| API·타입 | `apps/cs/src/entities/ntftalk/model/ntftalk-mmt-log-history-interface.ts`, `api/ntftalk-mmt-log-history-service.ts` |
|
||||||
|
| 엔드포인트 상수 | `apps/cs/src/shared/config/api.ts` — `ENDPOINT.NTFTALK_SERVICE.MMT_LOG_HISTORY` |
|
||||||
|
|
||||||
|
### 7-3. 영역별 UI 매핑
|
||||||
|
|
||||||
|
| 영역 | 화면 요소 | 컴포넌트·훅 | 구현 비고 |
|
||||||
|
| ---- | ------------ | ------------------------------------------------ | ---------------------------- |
|
||||||
|
| A | 검색 폼 | `Input`, `Label`, `Select`, `Button` | |
|
||||||
|
| B | 이력 그리드 | `useDataTable` + `ColumnDef`, `manualPagination` | |
|
||||||
|
| 공통 | 로딩·빈 목록 | `LoadingWithAnimation`, 빈 데이터 안내 | 기타 목록 페이지와 동일 수준 |
|
||||||
|
|
||||||
|
### 7-4. 데이터 흐름
|
||||||
|
|
||||||
|
```
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
// 최초 진입
|
||||||
|
CALL 알림톡발송이력목록조회(기본 검색 조건, page=0, size=10)
|
||||||
|
DISPLAY 알림톡 발송 이력 그리드 (영역 B)
|
||||||
|
|
||||||
|
ON [조회] 버튼 CLICK DO
|
||||||
|
CALL 알림톡발송이력목록조회(조회기간→dateFrom/dateTo, content, recipientNum, successYn, system, page=0)
|
||||||
|
DISPLAY 알림톡 발송 이력 그리드 (영역 B)
|
||||||
|
END ON
|
||||||
|
|
||||||
|
ON [초기화] 버튼 CLICK DO
|
||||||
|
RESET 검색 조건 → 기본값
|
||||||
|
END ON
|
||||||
|
|
||||||
|
END
|
||||||
|
```
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { dirname } from "path";
|
import { dirname } from "path";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { FlatCompat } from "@eslint/eslintrc";
|
import { FlatCompat } from "@eslint/eslintrc";
|
||||||
|
import baseEsLintConfig from "../../eslint.config.mjs";
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = dirname(__filename);
|
const __dirname = dirname(__filename);
|
||||||
@@ -11,6 +12,7 @@ const compat = new FlatCompat({
|
|||||||
|
|
||||||
const eslintConfig = [
|
const eslintConfig = [
|
||||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||||
|
...baseEsLintConfig,
|
||||||
{
|
{
|
||||||
rules: {
|
rules: {
|
||||||
"react-hooks/exhaustive-deps": "off",
|
"react-hooks/exhaustive-deps": "off",
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { client } from "@/shared/api";
|
||||||
|
import { ENDPOINT } from "@/shared/config";
|
||||||
|
import {
|
||||||
|
type NtftalkMmtLogHistory,
|
||||||
|
type NtftalkMmtLogHistoryPageList,
|
||||||
|
type NtftalkMmtLogHistorySavePayload,
|
||||||
|
type NtftalkMmtLogHistorySearchParams,
|
||||||
|
type NtftalkMmtLogHistoryService,
|
||||||
|
type NtftalkMmtLogHistoryUpdatePayload,
|
||||||
|
} from "../model/ntftalk-mmt-log-history-interface";
|
||||||
|
|
||||||
|
class NtftalkMmtLogHistoryApiService implements NtftalkMmtLogHistoryService {
|
||||||
|
async getNtftalkMmtLogHistoryPage(
|
||||||
|
params: NtftalkMmtLogHistorySearchParams,
|
||||||
|
): Promise<NtftalkMmtLogHistoryPageList> {
|
||||||
|
const response = await client.get<NtftalkMmtLogHistoryPageList>(
|
||||||
|
`${ENDPOINT.NTFTALK_SERVICE.MMT_LOG_HISTORY}`,
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getNtftalkMmtLogHistory(mtPr: number): Promise<NtftalkMmtLogHistory> {
|
||||||
|
const response = await client.get<NtftalkMmtLogHistory>(
|
||||||
|
`${ENDPOINT.NTFTALK_SERVICE.MMT_LOG_HISTORY}/${mtPr}`,
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveNtftalkMmtLogHistory(
|
||||||
|
payload: NtftalkMmtLogHistorySavePayload,
|
||||||
|
): Promise<NtftalkMmtLogHistory> {
|
||||||
|
const response = await client.post<NtftalkMmtLogHistory>(
|
||||||
|
`${ENDPOINT.NTFTALK_SERVICE.MMT_LOG_HISTORY}`,
|
||||||
|
payload,
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateNtftalkMmtLogHistory(
|
||||||
|
mtPr: number,
|
||||||
|
payload: NtftalkMmtLogHistoryUpdatePayload,
|
||||||
|
): Promise<NtftalkMmtLogHistory> {
|
||||||
|
const response = await client.put<NtftalkMmtLogHistory>(
|
||||||
|
`${ENDPOINT.NTFTALK_SERVICE.MMT_LOG_HISTORY}/${mtPr}`,
|
||||||
|
payload,
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteNtftalkMmtLogHistory(mtPr: number): Promise<void> {
|
||||||
|
await client.delete(
|
||||||
|
`${ENDPOINT.NTFTALK_SERVICE.MMT_LOG_HISTORY}/${mtPr}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NtftalkMmtLogHistoryApi = new NtftalkMmtLogHistoryApiService();
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
export { NtftalkVarApi } from "./api/ntftalk-var-service";
|
export { NtftalkVarApi } from "./api/ntftalk-var-service";
|
||||||
export { NtftalkTempApi } from "./api/ntftalk-temp-service";
|
export { NtftalkTempApi } from "./api/ntftalk-temp-service";
|
||||||
export { NtftalkBiztalkApi } from "./api/ntftalk-biztalk-service";
|
export { NtftalkBiztalkApi } from "./api/ntftalk-biztalk-service";
|
||||||
|
export { NtftalkMmtLogHistoryApi } from "./api/ntftalk-mmt-log-history-service";
|
||||||
export type {
|
export type {
|
||||||
NtftalkVar,
|
NtftalkVar,
|
||||||
NtftalkVarPageList,
|
NtftalkVarPageList,
|
||||||
@@ -27,3 +28,10 @@ export type {
|
|||||||
NtftalkTempSearchParams,
|
NtftalkTempSearchParams,
|
||||||
NtftalkTempUpdatePayload,
|
NtftalkTempUpdatePayload,
|
||||||
} from "./model/ntftalk-temp-interface";
|
} from "./model/ntftalk-temp-interface";
|
||||||
|
export type {
|
||||||
|
NtftalkMmtLogHistory,
|
||||||
|
NtftalkMmtLogHistoryPageList,
|
||||||
|
NtftalkMmtLogHistorySavePayload,
|
||||||
|
NtftalkMmtLogHistorySearchParams,
|
||||||
|
NtftalkMmtLogHistoryUpdatePayload,
|
||||||
|
} from "./model/ntftalk-mmt-log-history-interface";
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import type { PageableList, PageableParams } from "@/shared/model";
|
||||||
|
|
||||||
|
export interface NtftalkMmtLogHistorySearchParams extends PageableParams {
|
||||||
|
subject?: string;
|
||||||
|
content?: string;
|
||||||
|
recipientNum?: string;
|
||||||
|
/** 1:전송대기, 2:결과대기, 3:완료 */
|
||||||
|
msgStatus?: string;
|
||||||
|
/** 1000:성공 */
|
||||||
|
reportCode?: string;
|
||||||
|
/** yyyy-MM-dd */
|
||||||
|
dateFrom?: string;
|
||||||
|
/** yyyy-MM-dd */
|
||||||
|
dateTo?: string;
|
||||||
|
/** ADSS | CWMS | PORT | ALL */
|
||||||
|
system?: string;
|
||||||
|
/** SUCCESS | FAIL | ALL */
|
||||||
|
successYn?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NtftalkMmtLogHistory {
|
||||||
|
mtPr: number;
|
||||||
|
mtRefkey?: string | null;
|
||||||
|
priority?: string | null;
|
||||||
|
dateClientReq?: string | null;
|
||||||
|
subject?: string | null;
|
||||||
|
content?: string | null;
|
||||||
|
callback?: string | null;
|
||||||
|
msgStatus?: string | null;
|
||||||
|
recipientNum?: string | null;
|
||||||
|
dateMtSent?: string | null;
|
||||||
|
dateRslt?: string | null;
|
||||||
|
dateMtReport?: string | null;
|
||||||
|
reportCode?: string | null;
|
||||||
|
countryCode?: string | null;
|
||||||
|
msgType?: number | null;
|
||||||
|
regDate?: string | null;
|
||||||
|
senderKey?: string | null;
|
||||||
|
templateCode?: string | null;
|
||||||
|
siteSystem?: string | null;
|
||||||
|
successYn?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NtftalkMmtLogHistorySavePayload {
|
||||||
|
mtPr: number;
|
||||||
|
mtRefkey?: string;
|
||||||
|
priority?: string;
|
||||||
|
dateClientReq?: string;
|
||||||
|
subject: string;
|
||||||
|
content: string;
|
||||||
|
callback: string;
|
||||||
|
msgStatus?: string;
|
||||||
|
recipientNum: string;
|
||||||
|
reportCode?: string;
|
||||||
|
countryCode?: string;
|
||||||
|
msgType?: number;
|
||||||
|
senderKey: string;
|
||||||
|
templateCode?: string;
|
||||||
|
responseMethod?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NtftalkMmtLogHistoryUpdatePayload {
|
||||||
|
content: string;
|
||||||
|
recipientNum: string;
|
||||||
|
msgStatus?: string;
|
||||||
|
reportCode?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NtftalkMmtLogHistoryPageList = PageableList<NtftalkMmtLogHistory>;
|
||||||
|
|
||||||
|
export interface NtftalkMmtLogHistoryService {
|
||||||
|
getNtftalkMmtLogHistoryPage(
|
||||||
|
params: NtftalkMmtLogHistorySearchParams,
|
||||||
|
): Promise<NtftalkMmtLogHistoryPageList>;
|
||||||
|
getNtftalkMmtLogHistory(mtPr: number): Promise<NtftalkMmtLogHistory>;
|
||||||
|
saveNtftalkMmtLogHistory(
|
||||||
|
payload: NtftalkMmtLogHistorySavePayload,
|
||||||
|
): Promise<NtftalkMmtLogHistory>;
|
||||||
|
updateNtftalkMmtLogHistory(
|
||||||
|
mtPr: number,
|
||||||
|
payload: NtftalkMmtLogHistoryUpdatePayload,
|
||||||
|
): Promise<NtftalkMmtLogHistory>;
|
||||||
|
deleteNtftalkMmtLogHistory(mtPr: number): Promise<void>;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { NtftalkHistoryPage } from "./ui/ntftalk-history-page";
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import type { ColumnDef } from "@tanstack/react-table";
|
||||||
|
import type { NtftalkMmtLogHistory } from "@/entities/ntftalk";
|
||||||
|
|
||||||
|
function formatDateTime(value: string | null | undefined): string {
|
||||||
|
if (!value) return "—";
|
||||||
|
const normalized = value.includes("T")
|
||||||
|
? value.replace("T", " ").slice(0, 19)
|
||||||
|
: value.slice(0, 19);
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatResult(successYn: string | null | undefined): string {
|
||||||
|
if (!successYn) return "—";
|
||||||
|
if (successYn === "SUCCESS") return "성공";
|
||||||
|
if (successYn === "FAIL") return "실패";
|
||||||
|
return successYn;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createNtftalkHistoryColumns(): ColumnDef<NtftalkMmtLogHistory>[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: "rowNo",
|
||||||
|
header: "순번",
|
||||||
|
enableSorting: false,
|
||||||
|
cell: ({ row, table }) => {
|
||||||
|
const { pageIndex, pageSize } = table.getState().pagination;
|
||||||
|
return pageIndex * pageSize + row.index + 1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "siteSystem",
|
||||||
|
header: "시스템",
|
||||||
|
cell: ({ getValue }) => (getValue() as string) || "—",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "content",
|
||||||
|
header: "알림내용",
|
||||||
|
cell: ({ getValue }) => {
|
||||||
|
const v = getValue() as string;
|
||||||
|
if (!v) return "—";
|
||||||
|
return (
|
||||||
|
<span className="line-clamp-2 max-w-[28rem] text-left" title={v}>
|
||||||
|
{v}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "recipientNum",
|
||||||
|
header: "수신자",
|
||||||
|
cell: ({ getValue }) => (getValue() as string) || "—",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "dateRslt",
|
||||||
|
header: "수신일",
|
||||||
|
cell: ({ getValue }) => formatDateTime(getValue() as string | null),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "successYn",
|
||||||
|
header: "결과",
|
||||||
|
cell: ({ getValue }) => formatResult(getValue() as string | null),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useMemo, useState } from "react";
|
||||||
|
|
||||||
|
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||||
|
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
import { ENDPOINT } from "@/shared/config";
|
||||||
|
import { useDataTable } from "@/shared/lib";
|
||||||
|
import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker";
|
||||||
|
import {
|
||||||
|
NtftalkMmtLogHistoryApi,
|
||||||
|
type NtftalkMmtLogHistorySearchParams,
|
||||||
|
} from "@/entities/ntftalk";
|
||||||
|
import { SiteSelect } from "@/entities/site";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Input,
|
||||||
|
Label,
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@common/components";
|
||||||
|
|
||||||
|
import { createNtftalkHistoryColumns } from "../model/ntftalk-history-table-columns";
|
||||||
|
|
||||||
|
type DateRange = {
|
||||||
|
from?: Date | null;
|
||||||
|
to?: Date | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PresetPeriod = "" | "3M" | "6M" | "1Y" | "CUSTOM";
|
||||||
|
|
||||||
|
function computeDateRangeFromPreset(
|
||||||
|
preset: Extract<PresetPeriod, "3M" | "6M" | "1Y">,
|
||||||
|
): DateRange {
|
||||||
|
const to = dayjs().endOf("day").toDate();
|
||||||
|
const base = dayjs();
|
||||||
|
let fromDay: dayjs.Dayjs;
|
||||||
|
switch (preset) {
|
||||||
|
case "3M":
|
||||||
|
fromDay = base.subtract(3, "month");
|
||||||
|
break;
|
||||||
|
case "6M":
|
||||||
|
fromDay = base.subtract(6, "month");
|
||||||
|
break;
|
||||||
|
case "1Y":
|
||||||
|
fromDay = base.subtract(1, "year");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return { from: fromDay.startOf("day").toDate(), to };
|
||||||
|
}
|
||||||
|
|
||||||
|
function NtftalkHistoryPage() {
|
||||||
|
const [contentInput, setContentInput] = useState("");
|
||||||
|
const [recipientInput, setRecipientInput] = useState("");
|
||||||
|
const [presetPeriod, setPresetPeriod] = useState<PresetPeriod>("");
|
||||||
|
const [dateRange, setDateRange] = useState<DateRange>({
|
||||||
|
from: null,
|
||||||
|
to: null,
|
||||||
|
});
|
||||||
|
const [resultFilter, setResultFilter] = useState("ALL");
|
||||||
|
const [systemFilter, setSystemFilter] = useState("ALL");
|
||||||
|
|
||||||
|
const [searchParams, setSearchParams] =
|
||||||
|
useState<NtftalkMmtLogHistorySearchParams>({ sort: "dateMtReport,desc" });
|
||||||
|
const [pagination, setPagination] = useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
const listQuery = useQuery({
|
||||||
|
queryKey: [
|
||||||
|
ENDPOINT.NTFTALK_SERVICE.MMT_LOG_HISTORY,
|
||||||
|
searchParams,
|
||||||
|
pagination,
|
||||||
|
],
|
||||||
|
queryFn: () =>
|
||||||
|
NtftalkMmtLogHistoryApi.getNtftalkMmtLogHistoryPage({
|
||||||
|
...searchParams,
|
||||||
|
page: pagination.pageIndex,
|
||||||
|
size: pagination.pageSize,
|
||||||
|
}),
|
||||||
|
placeholderData: keepPreviousData,
|
||||||
|
});
|
||||||
|
|
||||||
|
const columns = useMemo(() => createNtftalkHistoryColumns(), []);
|
||||||
|
|
||||||
|
const { tableComponent } = useDataTable({
|
||||||
|
query: listQuery,
|
||||||
|
tableOptions: {
|
||||||
|
columns,
|
||||||
|
state: { pagination },
|
||||||
|
manualPagination: true,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
},
|
||||||
|
dataTableOptions: {
|
||||||
|
viewPagination: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const buildSearchParamsFromInputs = (): NtftalkMmtLogHistorySearchParams => ({
|
||||||
|
sort: searchParams.sort ?? "dateMtReport,desc",
|
||||||
|
content: contentInput.trim() || undefined,
|
||||||
|
recipientNum: recipientInput.trim() || undefined,
|
||||||
|
successYn: resultFilter,
|
||||||
|
system: systemFilter,
|
||||||
|
dateFrom: dateRange.from
|
||||||
|
? dayjs(dateRange.from).format("YYYY-MM-DD")
|
||||||
|
: undefined,
|
||||||
|
dateTo: dateRange.to ? dayjs(dateRange.to).format("YYYY-MM-DD") : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSearch = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setPagination((p) => ({ ...p, pageIndex: 0 }));
|
||||||
|
setSearchParams(buildSearchParamsFromInputs());
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePresetPeriodChange = (value: PresetPeriod) => {
|
||||||
|
setPresetPeriod(value);
|
||||||
|
if (value === "3M" || value === "6M" || value === "1Y") {
|
||||||
|
setDateRange(computeDateRangeFromPreset(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDateRangeChange = (next: DateRange) => {
|
||||||
|
setDateRange(next);
|
||||||
|
setPresetPeriod("CUSTOM");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
setContentInput("");
|
||||||
|
setRecipientInput("");
|
||||||
|
setPresetPeriod("");
|
||||||
|
setDateRange({ from: null, to: null });
|
||||||
|
setResultFilter("ALL");
|
||||||
|
setSystemFilter("ALL");
|
||||||
|
setPagination((p) => ({ ...p, pageIndex: 0 }));
|
||||||
|
setSearchParams({ sort: "dateMtReport,desc" });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="card card-border !mt-6 flex flex-col gap-4">
|
||||||
|
<div className="card card-border">
|
||||||
|
<form
|
||||||
|
onSubmit={handleSearch}
|
||||||
|
className="flex flex-col gap-4 p-2 md:flex-row md:items-end md:justify-between"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<div className="flex flex-wrap items-end gap-4">
|
||||||
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<Label className="whitespace-nowrap text-label">조회기간</Label>
|
||||||
|
<Select
|
||||||
|
value={presetPeriod}
|
||||||
|
onValueChange={handlePresetPeriodChange}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="w-[16rem]">
|
||||||
|
<SelectValue placeholder="기간 선택" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="3M">3개월</SelectItem>
|
||||||
|
<SelectItem value="6M">6개월</SelectItem>
|
||||||
|
<SelectItem value="1Y">1년</SelectItem>
|
||||||
|
<SelectItem value="CUSTOM">직접입력</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<InputDateRangePicker
|
||||||
|
dateRange={dateRange}
|
||||||
|
setDateRange={handleDateRangeChange}
|
||||||
|
disabled={presetPeriod !== "CUSTOM"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-6">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Label className="whitespace-nowrap text-label">알림내용</Label>
|
||||||
|
<Input
|
||||||
|
className="w-[16rem]"
|
||||||
|
value={contentInput}
|
||||||
|
onChange={(e) => setContentInput(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Label className="whitespace-nowrap text-label">사용자</Label>
|
||||||
|
<Input
|
||||||
|
className="w-[16rem]"
|
||||||
|
value={recipientInput}
|
||||||
|
onChange={(e) => setRecipientInput(e.target.value)}
|
||||||
|
placeholder="수신자 번호"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Label className="whitespace-nowrap text-label">결과</Label>
|
||||||
|
<Select value={resultFilter} onValueChange={setResultFilter}>
|
||||||
|
<SelectTrigger className="w-[16rem]">
|
||||||
|
<SelectValue placeholder="전체" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="ALL">전체</SelectItem>
|
||||||
|
<SelectItem value="SUCCESS">성공</SelectItem>
|
||||||
|
<SelectItem value="FAIL">실패</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Label className="whitespace-nowrap text-label">시스템</Label>
|
||||||
|
<SiteSelect
|
||||||
|
className="w-[16rem]"
|
||||||
|
value={systemFilter}
|
||||||
|
onValueChange={(value) => setSystemFilter(value)}
|
||||||
|
enabledAllSite
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex shrink-0 gap-2">
|
||||||
|
<Button className="h-[3.2rem]" type="submit">
|
||||||
|
조회
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
color="white"
|
||||||
|
className="h-[3.2rem]"
|
||||||
|
onClick={handleReset}
|
||||||
|
>
|
||||||
|
초기화
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card card-border">
|
||||||
|
<div className="card-header">
|
||||||
|
<h3 className="text-2xl font-medium text-label">알림톡 발송 이력</h3>
|
||||||
|
</div>
|
||||||
|
<div className="card !m-[1.2rem] !p-0">{tableComponent}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { NtftalkHistoryPage };
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
import { ENDPOINT } from "@/shared/config";
|
||||||
|
import { NoData } from "@/shared/ui/no-data";
|
||||||
|
import { NtftalkTempApi } from "@/entities/ntftalk";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -13,9 +17,6 @@ import {
|
|||||||
TableHead,
|
TableHead,
|
||||||
TableRow,
|
TableRow,
|
||||||
} from "@common/components";
|
} from "@common/components";
|
||||||
import { NtftalkTempApi } from "@/entities/ntftalk";
|
|
||||||
import { ENDPOINT } from "@/shared/config";
|
|
||||||
import { NoData } from "@/shared/ui/no-data";
|
|
||||||
|
|
||||||
interface NtftalkTempDetailDialogProps {
|
interface NtftalkTempDetailDialogProps {
|
||||||
templateCode: string | null;
|
templateCode: string | null;
|
||||||
|
|||||||
@@ -2,12 +2,38 @@
|
|||||||
|
|
||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useMemo, useState } from "react";
|
||||||
import { useFieldArray, useForm, useWatch } from "react-hook-form";
|
import { useFieldArray, useForm, useWatch } from "react-hook-form";
|
||||||
|
|
||||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||||
import {
|
import {
|
||||||
keepPreviousData,
|
keepPreviousData,
|
||||||
useQuery,
|
useQuery,
|
||||||
useQueryClient,
|
useQueryClient,
|
||||||
} from "@tanstack/react-query";
|
} from "@tanstack/react-query";
|
||||||
|
import {
|
||||||
|
getCoreRowModel,
|
||||||
|
type PaginationState,
|
||||||
|
type RowSelectionState,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { AUTH_USER_ID, ENDPOINT } from "@/shared/config";
|
||||||
|
import {
|
||||||
|
getCookie,
|
||||||
|
useAlertStore,
|
||||||
|
useDataTable,
|
||||||
|
useMutationWrapper,
|
||||||
|
} from "@/shared/lib";
|
||||||
|
import { NoData } from "@/shared/ui/no-data";
|
||||||
|
import {
|
||||||
|
NtftalkBiztalkApi,
|
||||||
|
type NtftalkBiztalkInspectionRequestItem,
|
||||||
|
type NtftalkTemp,
|
||||||
|
NtftalkTempApi,
|
||||||
|
type NtftalkTempSearchParams,
|
||||||
|
NtftalkVarApi,
|
||||||
|
type NtftalkVarSearchParams,
|
||||||
|
} from "@/entities/ntftalk";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
DataTable,
|
DataTable,
|
||||||
@@ -20,36 +46,14 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@common/components";
|
} from "@common/components";
|
||||||
import {
|
|
||||||
getCoreRowModel,
|
|
||||||
type PaginationState,
|
|
||||||
type RowSelectionState,
|
|
||||||
useReactTable,
|
|
||||||
} from "@tanstack/react-table";
|
|
||||||
import { z } from "zod";
|
|
||||||
import {
|
|
||||||
NtftalkBiztalkApi,
|
|
||||||
NtftalkTempApi,
|
|
||||||
NtftalkVarApi,
|
|
||||||
type NtftalkBiztalkInspectionRequestItem,
|
|
||||||
type NtftalkTemp,
|
|
||||||
type NtftalkTempSearchParams,
|
|
||||||
type NtftalkVarSearchParams,
|
|
||||||
} from "@/entities/ntftalk";
|
|
||||||
import { AUTH_USER_ID, ENDPOINT } from "@/shared/config";
|
|
||||||
import {
|
|
||||||
getCookie,
|
|
||||||
useAlertStore,
|
|
||||||
useDataTable,
|
|
||||||
useMutationWrapper,
|
|
||||||
} from "@/shared/lib";
|
|
||||||
import { NoData } from "@/shared/ui/no-data";
|
|
||||||
import { createTemplateColumns } from "../model/template-table-columns";
|
|
||||||
import {
|
import {
|
||||||
createEmptyVarDraft,
|
createEmptyVarDraft,
|
||||||
type NtftalkVarGridRow,
|
type NtftalkVarGridRow,
|
||||||
} from "../model/ntftalk-var-grid";
|
} from "../model/ntftalk-var-grid";
|
||||||
|
import { createTemplateColumns } from "../model/template-table-columns";
|
||||||
import { createVarColumns } from "../model/var-table-columns";
|
import { createVarColumns } from "../model/var-table-columns";
|
||||||
|
|
||||||
import { NtftalkTempDetailDialog } from "./ntftalk-temp-detail-dialog";
|
import { NtftalkTempDetailDialog } from "./ntftalk-temp-detail-dialog";
|
||||||
import { NtftalkTempRegistDialog } from "./ntftalk-temp-regist-dialog";
|
import { NtftalkTempRegistDialog } from "./ntftalk-temp-regist-dialog";
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import {
|
import {
|
||||||
keepPreviousData,
|
keepPreviousData,
|
||||||
useQuery,
|
useQuery,
|
||||||
@@ -13,10 +14,28 @@ import {
|
|||||||
type PaginationState,
|
type PaginationState,
|
||||||
type RowSelectionState,
|
type RowSelectionState,
|
||||||
} from "@tanstack/react-table";
|
} from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import { ENDPOINT } from "@/shared/config";
|
||||||
|
import { useAlertStore, useDataTable, useMutationWrapper } from "@/shared/lib";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/shared/ui/form";
|
||||||
|
import { NoData } from "@/shared/ui/no-data";
|
||||||
|
import {
|
||||||
|
NtftalkBiztalkApi,
|
||||||
|
NtftalkTempApi,
|
||||||
|
NtftalkVarApi,
|
||||||
|
type NtftalkVarSearchParams,
|
||||||
|
} from "@/entities/ntftalk";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
Input,
|
Input,
|
||||||
@@ -33,29 +52,13 @@ import {
|
|||||||
TableBody,
|
TableBody,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableHead,
|
TableHead,
|
||||||
Textarea,
|
|
||||||
TableRow,
|
TableRow,
|
||||||
DialogFooter,
|
Textarea,
|
||||||
} from "@common/components";
|
} from "@common/components";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
NtftalkBiztalkApi,
|
|
||||||
NtftalkTempApi,
|
|
||||||
NtftalkVarApi,
|
|
||||||
type NtftalkVarSearchParams,
|
|
||||||
} from "@/entities/ntftalk";
|
|
||||||
import { ENDPOINT } from "@/shared/config";
|
|
||||||
import { useAlertStore, useDataTable, useMutationWrapper } from "@/shared/lib";
|
|
||||||
import { NoData } from "@/shared/ui/no-data";
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormMessage,
|
|
||||||
} from "@/shared/ui/form";
|
|
||||||
import {
|
|
||||||
ntftalkTempSaveFormSchema,
|
|
||||||
type NtftalkTempSaveFormData,
|
type NtftalkTempSaveFormData,
|
||||||
|
ntftalkTempSaveFormSchema,
|
||||||
} from "../model/ntftalk-temp-save-schema";
|
} from "../model/ntftalk-temp-save-schema";
|
||||||
import { createVarColumns } from "../model/var-table-columns";
|
import { createVarColumns } from "../model/var-table-columns";
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ export const ENDPOINT = {
|
|||||||
NTFTALK_VAR: "/ntftalk-service/api/v1/ntftalk-var",
|
NTFTALK_VAR: "/ntftalk-service/api/v1/ntftalk-var",
|
||||||
NTFTALK_TEMP: "/ntftalk-service/api/v1/ntftalk-temp",
|
NTFTALK_TEMP: "/ntftalk-service/api/v1/ntftalk-temp",
|
||||||
NTFTALK_BIZTALK: "/ntftalk-service/api/v1/ntftalk-biztalk",
|
NTFTALK_BIZTALK: "/ntftalk-service/api/v1/ntftalk-biztalk",
|
||||||
|
MMT_LOG_HISTORY: "/ntftalk-service/api/v1/ata/mmt-log-history",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
import { InputDatePicker } from "@common/components";
|
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
import { InputDatePicker } from "@common/components";
|
||||||
|
|
||||||
import { useAlertStore } from "../lib";
|
import { useAlertStore } from "../lib";
|
||||||
|
|
||||||
type DateRange = {
|
type DateRange = {
|
||||||
from?: Date;
|
from?: Date | null;
|
||||||
to?: Date;
|
to?: Date | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
type InputDateRangePickerProps = {
|
type InputDateRangePickerProps = {
|
||||||
dateRange: DateRange;
|
dateRange: DateRange;
|
||||||
setDateRange: (dateRange: DateRange) => void;
|
setDateRange: (dateRange: DateRange) => void;
|
||||||
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
function InputDateRangePicker({
|
function InputDateRangePicker({
|
||||||
dateRange,
|
dateRange,
|
||||||
setDateRange,
|
setDateRange,
|
||||||
|
disabled,
|
||||||
}: InputDateRangePickerProps) {
|
}: InputDateRangePickerProps) {
|
||||||
const alert = useAlertStore((state) => state.setMessage);
|
const alert = useAlertStore((state) => state.setMessage);
|
||||||
|
|
||||||
@@ -32,6 +36,7 @@ function InputDateRangePicker({
|
|||||||
onChange={(date) => {
|
onChange={(date) => {
|
||||||
setDateRange({ ...dateRange, from: date });
|
setDateRange({ ...dateRange, from: date });
|
||||||
}}
|
}}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
~
|
~
|
||||||
<InputDatePicker
|
<InputDatePicker
|
||||||
@@ -39,6 +44,7 @@ function InputDateRangePicker({
|
|||||||
onChange={(date) => {
|
onChange={(date) => {
|
||||||
setDateRange({ ...dateRange, to: date });
|
setDateRange({ ...dateRange, to: date });
|
||||||
}}
|
}}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { Popover, PopoverContent, PopoverTrigger } from "../popover";
|
|||||||
import "./input-date-picker.css";
|
import "./input-date-picker.css";
|
||||||
|
|
||||||
export interface InputDatePickerProps {
|
export interface InputDatePickerProps {
|
||||||
value?: Date;
|
value?: Date | null;
|
||||||
defaultValue?: Date;
|
defaultValue?: Date;
|
||||||
onChange?: (date: Date | undefined) => void;
|
onChange?: (date: Date | undefined) => void;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
@@ -34,7 +34,7 @@ export function InputDatePicker({
|
|||||||
const [internalDate, setInternalDate] = useState<Date | undefined>(
|
const [internalDate, setInternalDate] = useState<Date | undefined>(
|
||||||
defaultValue,
|
defaultValue,
|
||||||
);
|
);
|
||||||
const [month, setMonth] = useState<Date | undefined>(
|
const [month, setMonth] = useState<Date | null | undefined>(
|
||||||
isControlled ? controlledValue : defaultValue,
|
isControlled ? controlledValue : defaultValue,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -112,9 +112,9 @@ export function InputDatePicker({
|
|||||||
>
|
>
|
||||||
<Calendar
|
<Calendar
|
||||||
mode="single"
|
mode="single"
|
||||||
selected={date}
|
selected={date ?? undefined}
|
||||||
captionLayout="dropdown"
|
captionLayout="dropdown"
|
||||||
month={month}
|
month={month ?? undefined}
|
||||||
onMonthChange={setMonth}
|
onMonthChange={setMonth}
|
||||||
onSelect={(newDate) => {
|
onSelect={(newDate) => {
|
||||||
handleDateChange(newDate);
|
handleDateChange(newDate);
|
||||||
|
|||||||
Reference in New Issue
Block a user