Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
182 changes: 180 additions & 2 deletions app/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -1735,7 +1735,9 @@ footer {
margin-top: 6px;
}
.basis-links a,
.report-details summary {
.report-details summary,
.fund-columns a,
.excluded-list a {
color: var(--report-accent);
}
.report-details {
Expand All @@ -1759,6 +1761,170 @@ footer {
font-weight: 800;
}

/* 정책자금 보고서 */
.fund-list {
display: grid;
gap: 16px;
}
.fund-card {
display: grid;
grid-template-columns: minmax(0, 1fr) 300px;
gap: 24px;
padding: 26px;
background: #fff;
border: 1px solid var(--report-line);
border-radius: 18px;
}
.fund-main {
min-width: 0;
}
.fund-kicker {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 0;
color: var(--muted);
font-size: 12px;
}
.fund-badge {
border-radius: 999px;
padding: 4px 10px;
background: #eceee9;
color: #4d5d57;
font-size: 11px;
font-weight: 800;
white-space: nowrap;
}
.fund-badge.status-current_candidate {
background: #e3f1ea;
color: #1f6b4c;
}
.fund-badge.status-needs_confirmation,
.fund-badge.status-post_registration {
background: #fbefd9;
color: #935612;
}
.fund-badge.status-not_eligible,
.fund-badge.status-closed,
.fund-badge.status-review_overdue {
background: #f7e6e1;
color: #9a3e24;
}
.fund-card h3 {
margin: 12px 0 14px;
font-size: 19px;
letter-spacing: -0.4px;
}
.fund-reason {
padding: 14px 16px;
border-radius: 12px;
background: var(--report-soft);
font-size: 13px;
}
.fund-reason.confirm {
background: var(--report-warm);
}
.fund-reason strong {
display: block;
margin-bottom: 4px;
font-size: 12px;
}
.fund-reason p,
.fund-reason ul {
margin: 0;
color: var(--report-text);
}
.fund-reason ul {
margin-top: 8px;
padding-left: 18px;
}
.fund-columns {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-top: 18px;
font-size: 13px;
}
.fund-columns h4 {
margin: 0 0 6px;
font-size: 12px;
}
.fund-columns p,
.fund-columns ul {
margin: 0 0 4px;
color: var(--report-text);
}
.fund-columns ul {
display: grid;
gap: 4px;
padding-left: 18px;
}
.fund-columns a {
font-size: 12px;
}
.fund-terms {
display: grid;
align-content: start;
gap: 10px;
margin: 0;
}
.fund-terms > div {
padding: 14px 16px;
background: var(--report-panel);
border: 1px solid var(--report-line);
border-radius: 12px;
}
.fund-terms dt {
color: var(--muted);
font-size: 12px;
}
.fund-terms dd {
margin: 4px 0 0;
font-size: 15px;
font-weight: 800;
}
.fund-terms small {
display: block;
margin-top: 4px;
color: var(--muted);
font-size: 11px;
}
.excluded-list {
list-style: none;
margin: 14px 0 0;
padding: 6px 26px;
background: #fff;
border: 1px solid var(--report-line);
border-radius: 18px;
}
.excluded-list li {
display: grid;
grid-template-columns: minmax(0, 280px) minmax(0, 1fr);
gap: 24px;
padding: 18px 0;
border-bottom: 1px solid var(--report-line);
}
.excluded-list li:last-child {
border-bottom: 0;
}
.excluded-list li > div:first-child strong {
display: block;
font-size: 14px;
}
.excluded-list li > div:first-child span {
color: var(--muted);
font-size: 12px;
}
.excluded-list p {
margin: 6px 0 4px;
color: var(--report-text);
font-size: 13px;
}
.excluded-list a {
font-size: 12px;
}

/* 상권 분석 보고서 */
.report-split {
display: grid;
Expand Down Expand Up @@ -2065,8 +2231,13 @@ footer {
.report-stats {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.report-split {
.report-split,
.fund-card {
grid-template-columns: 1fr;
}
.excluded-list li {
grid-template-columns: 1fr;
gap: 8px;
}
}
@media (max-width: 560px) {
Expand Down Expand Up @@ -2095,11 +2266,18 @@ footer {
.report-section {
padding: 20px 16px;
}
.fund-card {
padding: 18px;
}
.fund-columns,
.report-stats {
grid-template-columns: 1fr;
}
.picker-actions {
flex-direction: column;
align-items: stretch;
}
.excluded-list {
padding: 4px 16px;
}
}
74 changes: 40 additions & 34 deletions app/src/features/funding/FundingCandidatesPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ const numberFormat = new Intl.NumberFormat('ko-KR');

// 지원 유형은 대출과 다른 성격을 이름부터 구분한다. GRANT·SPACE·PROGRAM을
// 대출처럼 보이게 하는 표현을 쓰지 않는다.
const SUPPORT_TYPE_LABELS: Record<SupportType, string> = {
export const SUPPORT_TYPE_LABELS: Record<SupportType, string> = {
GRANT: '지원금(상환 없음)',
GUARANTEE: '보증(대출 아님)',
LOAN: '대출(상환 대상)',
SPACE: '공간·보육(대출 아님)',
PROGRAM: '프로그램(대출 아님)',
};

const STATUS_LABELS: Record<FundingCandidateStatus, string> = {
export const STATUS_LABELS: Record<FundingCandidateStatus, string> = {
CURRENT_CANDIDATE: '검토 후보',
NEEDS_CONFIRMATION: '추가 확인 필요',
NOT_ELIGIBLE: '조건 미충족',
Expand All @@ -35,15 +35,15 @@ const STATUS_LABELS: Record<FundingCandidateStatus, string> = {
REVIEW_OVERDUE: '검수 기한 경과',
};

const VERDICT_LABELS: Record<Verdict, string> = { PASS: '충족', FAIL: '미충족', UNKNOWN: '미확인' };
export const VERDICT_LABELS: Record<Verdict, string> = { PASS: '충족', FAIL: '미충족', UNKNOWN: '미확인' };

const APPLICATION_STATUS_LABELS: Record<ObservedApplicationStatus, string> = {
export const APPLICATION_STATUS_LABELS: Record<ObservedApplicationStatus, string> = {
OPEN: '접수 중으로 확인',
CLOSED: '접수 종료로 확인',
UNKNOWN: '접수 상태 미확인',
};

const REVIEW_STATE_LABELS: Record<ReviewState, string> = {
export const REVIEW_STATE_LABELS: Record<ReviewState, string> = {
CURRENT: '검수 최신',
UNREVIEWED: '검수자 미지정',
REVIEW_OVERDUE: '검수 기한 경과',
Expand Down Expand Up @@ -296,6 +296,40 @@ export function FundingCandidatesPanel({
);
}

export function ConditionTable({ evaluation }: { readonly evaluation: FundingCandidateEvaluation }) {
return (
<div className="table-scroll">
<table className="condition-table">
<caption>조건별 판정과 이유</caption>
<thead>
<tr>
<th scope="col">조건</th>
<th scope="col">판정</th>
<th scope="col">이유</th>
<th scope="col">근거</th>
</tr>
</thead>
<tbody>
{evaluation.conditions.map((condition) => (
<tr key={condition.key}>
<th scope="row">{condition.label}</th>
<td>
<span className={`verdict verdict-${condition.verdict.toLowerCase()}`}>
{VERDICT_LABELS[condition.verdict]}
</span>
</td>
<td className="reason-cell">{condition.detail}</td>
<td className="reason-cell">
{condition.evidenceIds.length === 0 ? '기록 없음' : condition.evidenceIds.join(', ')}
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}

function CandidateCard({
evaluation,
apply,
Expand Down Expand Up @@ -359,35 +393,7 @@ function CandidateCard({
</div>
</dl>

<div className="table-scroll">
<table className="condition-table">
<caption>조건별 판정과 이유</caption>
<thead>
<tr>
<th scope="col">조건</th>
<th scope="col">판정</th>
<th scope="col">이유</th>
<th scope="col">근거</th>
</tr>
</thead>
<tbody>
{evaluation.conditions.map((condition) => (
<tr key={condition.key}>
<th scope="row">{condition.label}</th>
<td>
<span className={`verdict verdict-${condition.verdict.toLowerCase()}`}>
{VERDICT_LABELS[condition.verdict]}
</span>
</td>
<td className="reason-cell">{condition.detail}</td>
<td className="reason-cell">
{condition.evidenceIds.length === 0 ? '기록 없음' : condition.evidenceIds.join(', ')}
</td>
</tr>
))}
</tbody>
</table>
</div>
<ConditionTable evaluation={evaluation} />

<div className="candidate-detail">
<div>
Expand Down
Loading
Loading