--- frontend/src/api/client.ts
+++ frontend/src/api/client.ts
... | ... | @@ -380,6 +380,9 @@ |
| 380 | 380 |
processTypes: DistributionItem[] |
| 381 | 381 |
processStatuses: DistributionItem[] |
| 382 | 382 |
changedKoglCount: number |
| 383 |
+ reviewFreeUseCount: number |
|
| 384 |
+ reviewNeedsProcessingCount: number |
|
| 385 |
+ reviewNoProcessingCount: number |
|
| 383 | 386 |
} |
| 384 | 387 |
} |
| 385 | 388 |
|
--- frontend/src/components/RightsDashboard.test.tsx
+++ frontend/src/components/RightsDashboard.test.tsx
... | ... | @@ -10,7 +10,7 @@ |
| 10 | 10 |
|
| 11 | 11 |
const data = {
|
| 12 | 12 |
summary: { applied: 2, docSubmitted: 0, reviewTotal: 120, reviewDone: 90, processTotal: 40, processDone: 10, unresolvedRe: 0, reportWriting: 0, completed: 0 },
|
| 13 |
- rights: { reviewResults: [{ label: '신유형개방', count: 90 }], reviewSourceKoglTypes: [{ label: '1유형', count: 120 }], reviewJudgedKoglTypes: [{ label: '1유형', count: 90 }], processTypes: [{ label: '0유형', count: 30 }, { label: '1유형', count: 10 }], processStatuses: [{ label: '진행중', count: 30 }, { label: '처리완료', count: 10 }], changedKoglCount: 6 },
|
|
| 13 |
+ rights: { reviewResults: [{ label: '신유형개방', count: 90 }], reviewSourceKoglTypes: [{ label: '1유형', count: 120 }], reviewJudgedKoglTypes: [{ label: '1유형', count: 90 }], processTypes: [{ label: '0유형', count: 30 }, { label: '1유형', count: 10 }], processStatuses: [{ label: '진행중', count: 30 }, { label: '처리완료', count: 10 }], changedKoglCount: 6, reviewFreeUseCount: 80, reviewNeedsProcessingCount: 7, reviewNoProcessingCount: 3 },
|
|
| 14 | 14 |
orgs: [ |
| 15 | 15 |
{ id: 1, orgNo: '001', orgName: '완료기관', stage: 7, hasChannel: true, lawyerName: null, mjName: null, lawyerAssignedDate: null, reviewTotal: 90, reviewDone: 90, processTotal: 10, processDone: 10, stageChangedAt: null, lastChangedAt: null, reCount: 0 },
|
| 16 | 16 |
{ id: 2, orgNo: '002', orgName: '대기기관', stage: 5, hasChannel: true, lawyerName: null, mjName: null, lawyerAssignedDate: null, reviewTotal: 30, reviewDone: 0, processTotal: 30, processDone: 0, stageChangedAt: null, lastChangedAt: null, reCount: 0 },
|
... | ... | @@ -28,6 +28,9 @@ |
| 28 | 28 |
expect(screen.getByText('권리확인 결과 유형별 현황')).toBeTruthy()
|
| 29 | 29 |
expect(screen.getByText('검토 대상 분류 현황')).toBeTruthy()
|
| 30 | 30 |
expect(screen.getByText('판정 공공누리유형 현황')).toBeTruthy()
|
| 31 |
+ expect(screen.getByText('자유이용 확정')).toBeTruthy()
|
|
| 32 |
+ expect(screen.getByText('처리필요 건수')).toBeTruthy()
|
|
| 33 |
+ expect(screen.getByText('권리처리 추진 미희망')).toBeTruthy()
|
|
| 31 | 34 |
}) |
| 32 | 35 |
|
| 33 | 36 |
it('기관 검색과 상태 필터가 동작한다', async () => {
|
--- frontend/src/components/RightsDashboard.tsx
+++ frontend/src/components/RightsDashboard.tsx
... | ... | @@ -94,7 +94,6 @@ |
| 94 | 94 |
const remaining = Math.max(total - done, 0) |
| 95 | 95 |
const rate = percent(done, total) |
| 96 | 96 |
const accent = isReview ? '#ea8a00' : '#078d2a' |
| 97 |
- const orgsWithWork = data.orgs.filter((row) => (isReview ? row.reviewTotal : row.processTotal) > 0).length |
|
| 98 | 97 |
const rights = data.rights |
| 99 | 98 |
|
| 100 | 99 |
return ( |
... | ... | @@ -113,13 +112,16 @@ |
| 113 | 112 |
))} |
| 114 | 113 |
</section> |
| 115 | 114 |
|
| 116 |
- <section className={`mt-3 grid gap-3 ${isReview ? 'lg:grid-cols-[1.2fr_1fr_1fr]' : 'lg:grid-cols-[1.1fr_1fr_1fr_1fr]'}`}>
|
|
| 115 |
+ <section className={`mt-3 grid gap-3 ${isReview ? 'lg:grid-cols-[1.15fr_repeat(4,1fr)]' : 'lg:grid-cols-[1.1fr_repeat(3,1fr)]'}`}>
|
|
| 117 | 116 |
<article className="flex items-center gap-5 rounded-lg border border-gray-200 bg-white p-5 shadow-sm"> |
| 118 | 117 |
<ProgressRing done={done} total={total} color={accent} />
|
| 119 | 118 |
<div><p className="text-xs font-medium text-gray-500">전체 진행률</p><p className="mt-1 text-lg font-bold">{rate}%</p><p className="mt-2 text-xs text-gray-500">잔여 {format(remaining)}건</p></div>
|
| 120 | 119 |
</article> |
| 121 |
- <article className="rounded-lg border border-gray-200 bg-white p-5 shadow-sm"><p className="text-xs font-medium text-gray-500">대상 기관</p><p className="mt-2 text-2xl font-bold tabular-nums">{orgsWithWork}</p><p className="mt-2 text-xs text-gray-500">{title} 자료가 등록된 기관</p></article>
|
|
| 122 |
- <article className="rounded-lg border border-gray-200 bg-white p-5 shadow-sm"><p className="text-xs font-medium text-gray-500">진행 기관</p><p className="mt-2 text-2xl font-bold tabular-nums">{data.orgs.filter((row) => { const t=isReview?row.reviewTotal:row.processTotal; const d=isReview?row.reviewDone:row.processDone; return d>0&&d<t }).length}</p><p className="mt-2 text-xs text-gray-500">일부 처리가 완료된 기관</p></article>
|
|
| 120 |
+ <article className="rounded-lg border border-gray-200 bg-white p-5 shadow-sm"><p className="text-xs font-medium text-gray-500">{title} 완료</p><p className="mt-2 text-2xl font-bold tabular-nums">{format(done)}</p><p className="mt-2 text-xs text-gray-500">전체 대상의 {rate}%</p><div className="mt-3 h-1 rounded-full bg-gray-200"><div className="h-full rounded-full" style={{width:`${rate}%`,backgroundColor:accent}} /></div></article>
|
|
| 121 |
+ {isReview && <article className="rounded-lg border border-gray-200 bg-white p-5 shadow-sm"><p className="text-xs font-medium text-gray-500">자유이용 확정</p><p className="mt-2 text-2xl font-bold tabular-nums">{format(rights?.reviewFreeUseCount ?? 0)}</p><p className="mt-2 text-xs text-gray-500">신유형 개방 판정 건수</p><div className="mt-3 h-1 rounded-full bg-gray-200"><div className="h-full rounded-full bg-green-600" style={{width:`${done ? Math.min((rights?.reviewFreeUseCount ?? 0)/done*100,100):0}%`}} /></div></article>}
|
|
| 122 |
+ {isReview && <article className="rounded-lg border border-gray-200 bg-white p-5 shadow-sm"><p className="text-xs font-medium text-gray-500">처리필요 건수</p><p className="mt-2 text-2xl font-bold tabular-nums">{format(rights?.reviewNeedsProcessingCount ?? 0)}</p><p className="mt-2 text-xs text-gray-500">권리처리필요 Y 판정 건수</p></article>}
|
|
| 123 |
+ {isReview && <article className="rounded-lg border border-gray-200 bg-white p-5 shadow-sm"><p className="text-xs font-medium text-gray-500">권리처리 추진 미희망</p><p className="mt-2 text-2xl font-bold tabular-nums">{format(rights?.reviewNoProcessingCount ?? 0)}</p><p className="mt-2 text-xs text-gray-500">기존 공공누리 유형 유지 건수</p></article>}
|
|
| 124 |
+ {!isReview && <article className="rounded-lg border border-gray-200 bg-white p-5 shadow-sm"><p className="text-xs font-medium text-gray-500">처리대상 건수(실제)</p><p className="mt-2 text-2xl font-bold tabular-nums">{format(total)}</p><p className="mt-2 text-xs text-gray-500">권리처리 자료 등록 건수</p></article>}
|
|
| 123 | 125 |
{!isReview && <article className="rounded-lg border border-gray-200 bg-white p-5 shadow-sm"><p className="text-xs font-medium text-gray-500">유형변경 건수</p><p className="mt-2 text-2xl font-bold tabular-nums text-pink-600">{format(rights?.changedKoglCount ?? 0)}</p><p className="mt-2 text-xs text-gray-500">기존 유형과 최종 판정 유형이 다른 건</p><div className="mt-3 h-1 rounded-full bg-gray-200"><div className="h-full rounded-full bg-pink-500" style={{width:`${total ? Math.min((rights?.changedKoglCount ?? 0)/total*100,100) : 0}%`}} /></div></article>}
|
| 124 | 126 |
</section> |
| 125 | 127 |
|
--- src/main/java/kr/itn/itnhub/dashboard/DashboardMapper.java
+++ src/main/java/kr/itn/itnhub/dashboard/DashboardMapper.java
... | ... | @@ -22,4 +22,7 @@ |
| 22 | 22 |
List<DistributionItem> processTypeDistribution(); |
| 23 | 23 |
List<DistributionItem> processStatusDistribution(); |
| 24 | 24 |
int countChangedKoglTypes(); |
| 25 |
+ int countReviewFreeUse(); |
|
| 26 |
+ int countReviewNeedsProcessing(); |
|
| 27 |
+ int countReviewNoProcessing(); |
|
| 25 | 28 |
} |
--- src/main/java/kr/itn/itnhub/dashboard/DashboardService.java
+++ src/main/java/kr/itn/itnhub/dashboard/DashboardService.java
... | ... | @@ -49,7 +49,10 @@ |
| 49 | 49 |
mapper.reviewJudgedKoglDistribution(), |
| 50 | 50 |
mapper.processTypeDistribution(), |
| 51 | 51 |
mapper.processStatusDistribution(), |
| 52 |
- mapper.countChangedKoglTypes()); |
|
| 52 |
+ mapper.countChangedKoglTypes(), |
|
| 53 |
+ mapper.countReviewFreeUse(), |
|
| 54 |
+ mapper.countReviewNeedsProcessing(), |
|
| 55 |
+ mapper.countReviewNoProcessing()); |
|
| 53 | 56 |
return new DashboardResponse(summarize(rows), rows, rights); |
| 54 | 57 |
} |
| 55 | 58 |
|
--- src/main/java/kr/itn/itnhub/dashboard/RightsDashboardStats.java
+++ src/main/java/kr/itn/itnhub/dashboard/RightsDashboardStats.java
... | ... | @@ -8,5 +8,8 @@ |
| 8 | 8 |
List<DistributionItem> reviewJudgedKoglTypes, |
| 9 | 9 |
List<DistributionItem> processTypes, |
| 10 | 10 |
List<DistributionItem> processStatuses, |
| 11 |
- int changedKoglCount) {
|
|
| 11 |
+ int changedKoglCount, |
|
| 12 |
+ int reviewFreeUseCount, |
|
| 13 |
+ int reviewNeedsProcessingCount, |
|
| 14 |
+ int reviewNoProcessingCount) {
|
|
| 12 | 15 |
} |
--- src/main/resources/mapper/DashboardMapper.xml
+++ src/main/resources/mapper/DashboardMapper.xml
... | ... | @@ -133,4 +133,16 @@ |
| 133 | 133 |
and coalesce(trim(prior_kogl_type), '') <> trim(judged_kogl_type) |
| 134 | 134 |
</select> |
| 135 | 135 |
|
| 136 |
+ <select id="countReviewFreeUse" resultType="int"> |
|
| 137 |
+ select count(*)::int from review_item where review_result = '신유형 개방' |
|
| 138 |
+ </select> |
|
| 139 |
+ |
|
| 140 |
+ <select id="countReviewNeedsProcessing" resultType="int"> |
|
| 141 |
+ select count(*)::int from review_item where upper(trim(coalesce(needs_processing, ''))) = 'Y' |
|
| 142 |
+ </select> |
|
| 143 |
+ |
|
| 144 |
+ <select id="countReviewNoProcessing" resultType="int"> |
|
| 145 |
+ select count(*)::int from review_item where review_result = '권리처리 추진 미희망' |
|
| 146 |
+ </select> |
|
| 147 |
+ |
|
| 136 | 148 |
</mapper> |
--- src/test/java/kr/itn/itnhub/dashboard/DashboardControllerTest.java
+++ src/test/java/kr/itn/itnhub/dashboard/DashboardControllerTest.java
... | ... | @@ -127,17 +127,20 @@ |
| 127 | 127 |
|
| 128 | 128 |
@Test |
| 129 | 129 |
void 권리관리_그래프는_DB의_분류값을_집계한다() throws Exception {
|
| 130 |
- jdbc.update("update review_item set kogl_type = '1유형', judged_kogl_type = '2유형' where org_id = ? and seq = 1", stage2Id);
|
|
| 130 |
+ jdbc.update("update review_item set kogl_type = '1유형', judged_kogl_type = '2유형', review_result = '신유형 개방', needs_processing = 'Y' where org_id = ? and seq = 1", stage2Id);
|
|
| 131 |
+ jdbc.update("update review_item set review_result = '권리처리 추진 미희망' where org_id = ? and seq = 2", stage2Id);
|
|
| 131 | 132 |
jdbc.update("update process_item set judged_kogl_type = '1유형', prior_kogl_type = '0유형' where org_id = ? and seq = 1", stage2Id);
|
| 132 | 133 |
|
| 133 | 134 |
mvc.perform(get("/api/dashboard"))
|
| 134 | 135 |
.andExpect(status().isOk()) |
| 135 |
- .andExpect(jsonPath("$.rights.reviewResults[0].label").value("미확인"))
|
|
| 136 | 136 |
.andExpect(jsonPath("$.rights.reviewSourceKoglTypes[0].label").value("미부착"))
|
| 137 | 137 |
.andExpect(jsonPath("$.rights.reviewJudgedKoglTypes[0].label").value("2유형"))
|
| 138 | 138 |
.andExpect(jsonPath("$.rights.processTypes.length()").value(2))
|
| 139 | 139 |
.andExpect(jsonPath("$.rights.processStatuses.length()").value(2))
|
| 140 |
- .andExpect(jsonPath("$.rights.changedKoglCount").value(1));
|
|
| 140 |
+ .andExpect(jsonPath("$.rights.changedKoglCount").value(1))
|
|
| 141 |
+ .andExpect(jsonPath("$.rights.reviewFreeUseCount").value(1))
|
|
| 142 |
+ .andExpect(jsonPath("$.rights.reviewNeedsProcessingCount").value(1))
|
|
| 143 |
+ .andExpect(jsonPath("$.rights.reviewNoProcessingCount").value(1));
|
|
| 141 | 144 |
} |
| 142 | 145 |
|
| 143 | 146 |
@Test |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?