feat:extended BZO information
This commit is contained in:
parent
b142b93666
commit
1f87e00339
3 changed files with 1480 additions and 245 deletions
|
|
@ -125,6 +125,24 @@
|
||||||
color: var(--color-text-secondary, #6b7280);
|
color: var(--color-text-secondary, #6b7280);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.documentsSection {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
border-top: 2px solid var(--color-primary, #3b82f6);
|
||||||
|
background-color: var(--color-bg-secondary, #f9fafb);
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-left: -1.5rem;
|
||||||
|
margin-right: -1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.documentsSectionTitle {
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
}
|
||||||
|
|
||||||
.infoGrid {
|
.infoGrid {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -241,6 +259,670 @@
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.documentsList {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.documentLink {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: var(--color-bg, #ffffff);
|
||||||
|
border: 2px solid var(--color-primary, #3b82f6);
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.documentLink:hover {
|
||||||
|
background-color: var(--color-primary-light, #eff6ff);
|
||||||
|
border-color: var(--color-primary-dark, #2563eb);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 6px rgba(59, 130, 246, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.documentLink:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.documentLabel {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-primary, #3b82f6);
|
||||||
|
font-size: 1rem;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.documentLink:hover .documentLabel {
|
||||||
|
color: var(--color-primary-dark, #2563eb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.documentType {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--color-text-secondary, #6b7280);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BZO Information Styles */
|
||||||
|
.bzoButtonContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoButton {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background-color: var(--color-primary, #3b82f6);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoButton:hover:not(:disabled) {
|
||||||
|
background-color: var(--color-primary-dark, #2563eb);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoButton:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.errorMessage {
|
||||||
|
color: var(--color-error, #ef4444);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background-color: var(--color-error-light, #fee2e2);
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--color-error, #ef4444);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoSection {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
border-top: 2px solid var(--color-primary, #3b82f6);
|
||||||
|
background-color: var(--color-bg-secondary, #f9fafb);
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-left: -1.5rem;
|
||||||
|
margin-right: -1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoHeader {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoSectionTitle {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleButton {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--color-text-secondary, #6b7280);
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleButton:hover {
|
||||||
|
background-color: var(--color-hover, #f3f4f6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoContent {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoSubSection {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoSubTitle {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
border-bottom: 1px solid var(--color-border, #e5e7eb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoSummary {
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: var(--color-bg, #ffffff);
|
||||||
|
border-left: 4px solid var(--color-primary, #3b82f6);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Markdown Styles for BZO Content */
|
||||||
|
.bzoMarkdown {
|
||||||
|
line-height: 1.6;
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownH1,
|
||||||
|
.bzoMarkdownH2,
|
||||||
|
.bzoMarkdownH3,
|
||||||
|
.bzoMarkdownH4,
|
||||||
|
.bzoMarkdownH5,
|
||||||
|
.bzoMarkdownH6 {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownH1 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
border-bottom: 2px solid var(--color-border, #e5e7eb);
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownH2 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
border-bottom: 1px solid var(--color-border, #e5e7eb);
|
||||||
|
padding-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownH3 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownH4 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownH5 {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownH6 {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownP {
|
||||||
|
margin: 0.75rem 0;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownP:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownP:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownUl,
|
||||||
|
.bzoMarkdownOl {
|
||||||
|
margin: 0.75rem 0;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownLi {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownUl .bzoMarkdownLi {
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownOl .bzoMarkdownLi {
|
||||||
|
list-style-type: decimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownTableWrapper {
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownTable {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1px solid var(--color-border, #e5e7eb);
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownThead {
|
||||||
|
background-color: var(--color-bg-secondary, #f9fafb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownTh {
|
||||||
|
padding: 0.75rem;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
|
border-bottom: 2px solid var(--color-border, #e5e7eb);
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownTd {
|
||||||
|
padding: 0.75rem;
|
||||||
|
border-bottom: 1px solid var(--color-border, #e5e7eb);
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownTr:last-child .bzoMarkdownTd {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownTr:hover {
|
||||||
|
background-color: var(--color-hover, #f3f4f6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownCodeInline {
|
||||||
|
background-color: var(--color-bg-secondary, #f9fafb);
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: var(--color-primary, #3b82f6);
|
||||||
|
border: 1px solid var(--color-border, #e5e7eb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownPre {
|
||||||
|
background-color: var(--color-bg-secondary, #f9fafb);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow-x: auto;
|
||||||
|
border: 1px solid var(--color-border, #e5e7eb);
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownCodeBlock {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownBlockquote {
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border-left: 4px solid var(--color-primary, #3b82f6);
|
||||||
|
background-color: var(--color-bg-secondary, #f9fafb);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--color-text-secondary, #6b7280);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownStrong {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownEm {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownLink {
|
||||||
|
color: var(--color-primary, #3b82f6);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownLink:hover {
|
||||||
|
color: var(--color-primary-dark, #2563eb);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoMarkdownHr {
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid var(--color-border, #e5e7eb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoInfoGrid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoInfoItem {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoLabel {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-secondary, #6b7280);
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoValue {
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoZonesList {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoZoneCard {
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: var(--color-bg, #ffffff);
|
||||||
|
border: 1px solid var(--color-border, #e5e7eb);
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoZoneCard:hover {
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoZoneHeader {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
border-bottom: 1px solid var(--color-border, #e5e7eb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoZoneCode {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: var(--color-primary, #3b82f6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoZoneName {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoZoneDetails {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoZoneDetailItem {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoDetailLabel {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-secondary, #6b7280);
|
||||||
|
min-width: 140px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoDetailValue {
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoRulesList {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoRuleCard {
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: var(--color-bg, #ffffff);
|
||||||
|
border: 1px solid var(--color-border, #e5e7eb);
|
||||||
|
border-left: 4px solid var(--color-primary, #3b82f6);
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoRuleCard:hover {
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoRuleHeader {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoRuleType {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
text-transform: capitalize;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoConfidence {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--color-text-secondary, #6b7280);
|
||||||
|
background-color: var(--color-bg-secondary, #f9fafb);
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoRuleValue {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoRuleNumeric {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-primary, #3b82f6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoRuleText {
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoRuleSnippet {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background-color: var(--color-bg-secondary, #f9fafb);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--color-text-secondary, #6b7280);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoRuleZone,
|
||||||
|
.bzoRulePage {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--color-text-secondary, #6b7280);
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoRuleMeta {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
border-top: 1px solid var(--color-border, #e5e7eb);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--color-text-secondary, #6b7280);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoArticlesList {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoArticleCard {
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: var(--color-bg, #ffffff);
|
||||||
|
border: 1px solid var(--color-border, #e5e7eb);
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoArticleCard:hover {
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoArticleHeader {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
border-bottom: 1px solid var(--color-border, #e5e7eb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoArticleLabel {
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--color-primary, #3b82f6);
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoArticleTitle {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoArticleText {
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoArticleMeta {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--color-text-secondary, #6b7280);
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
border-top: 1px solid var(--color-border, #e5e7eb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoDocumentsList {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoDocumentItem {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.75rem;
|
||||||
|
background-color: var(--color-bg, #ffffff);
|
||||||
|
border: 1px solid var(--color-border, #e5e7eb);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoDocumentLabel {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoDocumentType {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--color-text-secondary, #6b7280);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoErrors,
|
||||||
|
.bzoWarnings {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoErrorTitle {
|
||||||
|
margin: 0 0 0.5rem 0;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-error, #ef4444);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoWarningTitle {
|
||||||
|
margin: 0 0 0.5rem 0;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #f59e0b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoErrorList,
|
||||||
|
.bzoWarningList {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
color: var(--color-text, #111827);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoErrorList li {
|
||||||
|
color: var(--color-error, #ef4444);
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoWarningList li {
|
||||||
|
color: #f59e0b;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoStats {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoStatItem {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoStatLabel {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-text-secondary, #6b7280);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bzoStatValue {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--color-primary, #3b82f6);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.panel {
|
.panel {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { FaTimes, FaTrash } from 'react-icons/fa';
|
import { FaTimes, FaTrash, FaInfoCircle, FaSpinner } from 'react-icons/fa';
|
||||||
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import remarkGfm from 'remark-gfm';
|
||||||
|
import { ContentPreview } from '../../ContentPreview';
|
||||||
|
import api from '../../../api';
|
||||||
import styles from './ParcelInfoPanel.module.css';
|
import styles from './ParcelInfoPanel.module.css';
|
||||||
|
|
||||||
export interface ParcelInfoPanelProps {
|
export interface ParcelInfoPanelProps {
|
||||||
|
|
@ -11,6 +15,77 @@ export interface ParcelInfoPanelProps {
|
||||||
adjacentParcels?: any[];
|
adjacentParcels?: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BZO Information Types
|
||||||
|
interface BZOGemeinde {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
plz: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BZOZone {
|
||||||
|
zone_code: string;
|
||||||
|
zone_name: string;
|
||||||
|
zone_category: string;
|
||||||
|
geschosszahl: number;
|
||||||
|
gewerbeerleichterung: boolean;
|
||||||
|
source_article: string;
|
||||||
|
page: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BZORule {
|
||||||
|
rule_type: string;
|
||||||
|
value_numeric?: number;
|
||||||
|
value_text: string;
|
||||||
|
unit?: string;
|
||||||
|
condition_text?: string | null;
|
||||||
|
is_table_rule: boolean;
|
||||||
|
table_zones: string[];
|
||||||
|
page: number;
|
||||||
|
text_snippet: string;
|
||||||
|
zone_raw: string;
|
||||||
|
rule_scope: string;
|
||||||
|
confidence: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BZOArticle {
|
||||||
|
article_label: string;
|
||||||
|
article_title: string;
|
||||||
|
text: string;
|
||||||
|
page_start: number;
|
||||||
|
page_end: number;
|
||||||
|
section_level_1?: string | null;
|
||||||
|
section_level_2?: string | null;
|
||||||
|
section_level_3?: string | null;
|
||||||
|
zone_raw: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BZOExtractedContent {
|
||||||
|
zones: BZOZone[];
|
||||||
|
rules: BZORule[];
|
||||||
|
articles: BZOArticle[];
|
||||||
|
total_zones: number;
|
||||||
|
total_rules: number;
|
||||||
|
total_articles: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BZODocument {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
dokumentTyp: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BZOInformationResponse {
|
||||||
|
parcel_id: string;
|
||||||
|
bauzone: string;
|
||||||
|
gemeinde: BZOGemeinde;
|
||||||
|
extracted_content: BZOExtractedContent;
|
||||||
|
ai_summary: string;
|
||||||
|
relevant_rules: BZORule[];
|
||||||
|
documents_processed: BZODocument[];
|
||||||
|
errors: string[];
|
||||||
|
warnings: string[];
|
||||||
|
}
|
||||||
|
|
||||||
const ParcelInfoPanel: React.FC<ParcelInfoPanelProps> = ({
|
const ParcelInfoPanel: React.FC<ParcelInfoPanelProps> = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
|
|
@ -18,9 +93,75 @@ const ParcelInfoPanel: React.FC<ParcelInfoPanelProps> = ({
|
||||||
onRemoveParcel,
|
onRemoveParcel,
|
||||||
adjacentParcels = []
|
adjacentParcels = []
|
||||||
}) => {
|
}) => {
|
||||||
|
// State for document preview popup
|
||||||
|
const [previewDocument, setPreviewDocument] = useState<{
|
||||||
|
fileId: string;
|
||||||
|
fileName: string;
|
||||||
|
mimeType: string;
|
||||||
|
} | null>(null);
|
||||||
|
|
||||||
|
// State for BZO information
|
||||||
|
const [bzoInfo, setBzoInfo] = useState<Record<string, BZOInformationResponse | null>>({});
|
||||||
|
const [loadingBzo, setLoadingBzo] = useState<Record<string, boolean>>({});
|
||||||
|
const [bzoError, setBzoError] = useState<Record<string, string>>({});
|
||||||
|
const [expandedBzo, setExpandedBzo] = useState<Record<string, boolean>>({});
|
||||||
|
|
||||||
|
// Fetch BZO information
|
||||||
|
const fetchBZOInformation = async (parcelData: any) => {
|
||||||
|
// Extract gemeinde and bauzone from parcel data
|
||||||
|
const gemeinde = parcelData.gemeinde?.label || parcelData.parcel?.municipality_name;
|
||||||
|
const bauzone = parcelData.parcel?.bauzone;
|
||||||
|
|
||||||
|
// Use parcel ID as key for state management
|
||||||
|
const parcelKey = parcelData.parcel?.id || parcelData.parcel?.number || 'unknown';
|
||||||
|
|
||||||
|
// Validate required fields
|
||||||
|
if (!gemeinde) {
|
||||||
|
const errorMsg = 'Gemeinde-Information fehlt. BZO-Informationen können nicht abgerufen werden.';
|
||||||
|
setBzoError(prev => ({ ...prev, [parcelKey]: errorMsg }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bauzone) {
|
||||||
|
const errorMsg = 'Bauzone-Information fehlt. BZO-Informationen können nicht abgerufen werden.';
|
||||||
|
setBzoError(prev => ({ ...prev, [parcelKey]: errorMsg }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadingBzo[parcelKey] || bzoInfo[parcelKey]) return; // Already loading or loaded
|
||||||
|
|
||||||
|
setLoadingBzo(prev => ({ ...prev, [parcelKey]: true }));
|
||||||
|
setBzoError(prev => ({ ...prev, [parcelKey]: '' }));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await api.get<BZOInformationResponse>(
|
||||||
|
'/api/realestate/bzo-information',
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
gemeinde: gemeinde,
|
||||||
|
bauzone: bauzone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
setBzoInfo(prev => ({ ...prev, [parcelKey]: response.data }));
|
||||||
|
setExpandedBzo(prev => ({ ...prev, [parcelKey]: true }));
|
||||||
|
} catch (error: any) {
|
||||||
|
const errorMessage = error.response?.data?.detail || error.response?.data?.message || error.message || 'Fehler beim Laden der BZO-Informationen';
|
||||||
|
setBzoError(prev => ({ ...prev, [parcelKey]: errorMessage }));
|
||||||
|
console.error('Error fetching BZO information:', error);
|
||||||
|
} finally {
|
||||||
|
setLoadingBzo(prev => ({ ...prev, [parcelKey]: false }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleBZOExpanded = (parcelId: string) => {
|
||||||
|
setExpandedBzo(prev => ({ ...prev, [parcelId]: !prev[parcelId] }));
|
||||||
|
};
|
||||||
|
|
||||||
if (!parcels || parcels.length === 0) return null;
|
if (!parcels || parcels.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<>
|
<>
|
||||||
|
|
@ -148,6 +289,39 @@ const ParcelInfoPanel: React.FC<ParcelInfoPanelProps> = ({
|
||||||
<span className={styles.value}>{parcelData.parcel.bauzone}</span>
|
<span className={styles.value}>{parcelData.parcel.bauzone}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{parcelData.parcel.id && (
|
||||||
|
<div className={styles.infoItem}>
|
||||||
|
<span className={styles.label}>BZO-Informationen:</span>
|
||||||
|
<div className={styles.bzoButtonContainer}>
|
||||||
|
{(() => {
|
||||||
|
const parcelKey = parcelData.parcel.id || parcelData.parcel.number || 'unknown';
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{!bzoInfo[parcelKey] && !loadingBzo[parcelKey] && (
|
||||||
|
<button
|
||||||
|
className={styles.bzoButton}
|
||||||
|
onClick={() => fetchBZOInformation(parcelData)}
|
||||||
|
title="BZO-Informationen abrufen"
|
||||||
|
>
|
||||||
|
<FaInfoCircle /> BZO-Informationen laden
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{loadingBzo[parcelKey] && (
|
||||||
|
<button className={styles.bzoButton} disabled>
|
||||||
|
<FaSpinner className={styles.spinner} /> Lädt...
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{bzoError[parcelKey] && (
|
||||||
|
<div className={styles.errorMessage}>
|
||||||
|
{bzoError[parcelKey]}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{parcelData.parcel.zone && Array.isArray(parcelData.parcel.zone) && parcelData.parcel.zone.length > 0 && (
|
{parcelData.parcel.zone && Array.isArray(parcelData.parcel.zone) && parcelData.parcel.zone.length > 0 && (
|
||||||
<div className={styles.infoItem}>
|
<div className={styles.infoItem}>
|
||||||
<span className={styles.label}>Zone:</span>
|
<span className={styles.label}>Zone:</span>
|
||||||
|
|
@ -203,37 +377,62 @@ const ParcelInfoPanel: React.FC<ParcelInfoPanelProps> = ({
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{parcelData.gemeinde && (
|
||||||
|
<div className={styles.infoItem}>
|
||||||
|
<span className={styles.label}>Gemeinde:</span>
|
||||||
|
<span className={styles.value}>
|
||||||
|
{parcelData.gemeinde.label}
|
||||||
|
{parcelData.gemeinde.plz && ` (${parcelData.gemeinde.plz})`}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Map View Info for this parcel */}
|
{/* BZO Information Section */}
|
||||||
{parcelData.map_view && (
|
{(() => {
|
||||||
<div className={styles.mapViewSection}>
|
const parcelKey = parcelData.parcel.id || parcelData.parcel.number || 'unknown';
|
||||||
<h4 className={styles.subSectionTitle}>Kartenansicht</h4>
|
return bzoInfo[parcelKey] && (
|
||||||
<div className={styles.infoGrid}>
|
<div className={styles.bzoSection}>
|
||||||
{parcelData.map_view.center && (
|
<div className={styles.bzoHeader}>
|
||||||
<div className={styles.infoItem}>
|
<h4 className={styles.bzoSectionTitle}>BZO-Informationen</h4>
|
||||||
<span className={styles.label}>Zentrum:</span>
|
<button
|
||||||
<span className={styles.value}>
|
className={styles.toggleButton}
|
||||||
{parcelData.map_view.center.x.toFixed(2)}, {parcelData.map_view.center.y.toFixed(2)}
|
onClick={() => toggleBZOExpanded(parcelKey)}
|
||||||
</span>
|
>
|
||||||
|
{expandedBzo[parcelKey] ? '▼' : '▶'}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{expandedBzo[parcelKey] && (
|
||||||
|
<BZOInformationDisplay data={bzoInfo[parcelKey]!} />
|
||||||
)}
|
)}
|
||||||
{parcelData.map_view.zoom_bounds && (
|
|
||||||
<>
|
|
||||||
<div className={styles.infoItem}>
|
|
||||||
<span className={styles.label}>Bounds Min:</span>
|
|
||||||
<span className={styles.value}>
|
|
||||||
{parcelData.map_view.zoom_bounds.min_x.toFixed(2)}, {parcelData.map_view.zoom_bounds.min_y.toFixed(2)}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.infoItem}>
|
);
|
||||||
<span className={styles.label}>Bounds Max:</span>
|
})()}
|
||||||
<span className={styles.value}>
|
|
||||||
{parcelData.map_view.zoom_bounds.max_x.toFixed(2)}, {parcelData.map_view.zoom_bounds.max_y.toFixed(2)}
|
{/* Documents Section */}
|
||||||
</span>
|
{parcelData.documents && Array.isArray(parcelData.documents) && parcelData.documents.length > 0 && (
|
||||||
</div>
|
<div className={styles.documentsSection}>
|
||||||
</>
|
<h4 className={styles.documentsSectionTitle}>Dokumente ({parcelData.documents.length})</h4>
|
||||||
|
<div className={styles.documentsList}>
|
||||||
|
{parcelData.documents.map((document) => (
|
||||||
|
<button
|
||||||
|
key={document.id}
|
||||||
|
className={styles.documentLink}
|
||||||
|
onClick={() => {
|
||||||
|
setPreviewDocument({
|
||||||
|
fileId: document.dokumentReferenz,
|
||||||
|
fileName: document.label,
|
||||||
|
mimeType: document.mimeType
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
title={`${document.label} (${document.dokumentTyp})`}
|
||||||
|
>
|
||||||
|
<span className={styles.documentLabel}>{document.label}</span>
|
||||||
|
{document.dokumentTyp && (
|
||||||
|
<span className={styles.documentType}>{document.dokumentTyp}</span>
|
||||||
)}
|
)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -268,6 +467,347 @@ const ParcelInfoPanel: React.FC<ParcelInfoPanelProps> = ({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
||||||
|
{/* Document Preview Popup */}
|
||||||
|
{previewDocument && (
|
||||||
|
<ContentPreview
|
||||||
|
isOpen={!!previewDocument}
|
||||||
|
onClose={() => setPreviewDocument(null)}
|
||||||
|
fileId={previewDocument.fileId}
|
||||||
|
fileName={previewDocument.fileName}
|
||||||
|
mimeType={previewDocument.mimeType}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// BZO Information Display Component
|
||||||
|
interface BZOInformationDisplayProps {
|
||||||
|
data: BZOInformationResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BZOInformationDisplay: React.FC<BZOInformationDisplayProps> = ({ data }) => {
|
||||||
|
return (
|
||||||
|
<div className={styles.bzoContent}>
|
||||||
|
{/* Summary Section */}
|
||||||
|
{data.ai_summary && (
|
||||||
|
<div className={styles.bzoSubSection}>
|
||||||
|
<h5 className={styles.bzoSubTitle}>Zusammenfassung</h5>
|
||||||
|
<div className={styles.bzoSummary}>
|
||||||
|
<ReactMarkdown
|
||||||
|
remarkPlugins={[remarkGfm]}
|
||||||
|
className={styles.bzoMarkdown}
|
||||||
|
components={{
|
||||||
|
h1: ({node, ...props}) => <h1 className={styles.bzoMarkdownH1} {...props} />,
|
||||||
|
h2: ({node, ...props}) => <h2 className={styles.bzoMarkdownH2} {...props} />,
|
||||||
|
h3: ({node, ...props}) => <h3 className={styles.bzoMarkdownH3} {...props} />,
|
||||||
|
h4: ({node, ...props}) => <h4 className={styles.bzoMarkdownH4} {...props} />,
|
||||||
|
h5: ({node, ...props}) => <h5 className={styles.bzoMarkdownH5} {...props} />,
|
||||||
|
h6: ({node, ...props}) => <h6 className={styles.bzoMarkdownH6} {...props} />,
|
||||||
|
p: ({node, ...props}) => <p className={styles.bzoMarkdownP} {...props} />,
|
||||||
|
ul: ({node, ...props}) => <ul className={styles.bzoMarkdownUl} {...props} />,
|
||||||
|
ol: ({node, ...props}) => <ol className={styles.bzoMarkdownOl} {...props} />,
|
||||||
|
li: ({node, ...props}) => <li className={styles.bzoMarkdownLi} {...props} />,
|
||||||
|
table: ({node, ...props}) => <div className={styles.bzoMarkdownTableWrapper}><table className={styles.bzoMarkdownTable} {...props} /></div>,
|
||||||
|
thead: ({node, ...props}) => <thead className={styles.bzoMarkdownThead} {...props} />,
|
||||||
|
tbody: ({node, ...props}) => <tbody className={styles.bzoMarkdownTbody} {...props} />,
|
||||||
|
tr: ({node, ...props}) => <tr className={styles.bzoMarkdownTr} {...props} />,
|
||||||
|
th: ({node, ...props}) => <th className={styles.bzoMarkdownTh} {...props} />,
|
||||||
|
td: ({node, ...props}) => <td className={styles.bzoMarkdownTd} {...props} />,
|
||||||
|
code: ({node, inline, ...props}: any) =>
|
||||||
|
inline ? (
|
||||||
|
<code className={styles.bzoMarkdownCodeInline} {...props} />
|
||||||
|
) : (
|
||||||
|
<code className={styles.bzoMarkdownCodeBlock} {...props} />
|
||||||
|
),
|
||||||
|
pre: ({node, ...props}) => <pre className={styles.bzoMarkdownPre} {...props} />,
|
||||||
|
blockquote: ({node, ...props}) => <blockquote className={styles.bzoMarkdownBlockquote} {...props} />,
|
||||||
|
strong: ({node, ...props}) => <strong className={styles.bzoMarkdownStrong} {...props} />,
|
||||||
|
em: ({node, ...props}) => <em className={styles.bzoMarkdownEm} {...props} />,
|
||||||
|
a: ({node, ...props}: any) => <a className={styles.bzoMarkdownLink} {...props} />,
|
||||||
|
hr: ({node, ...props}) => <hr className={styles.bzoMarkdownHr} {...props} />,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{data.ai_summary}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Gemeinde Info */}
|
||||||
|
{data.gemeinde && (
|
||||||
|
<div className={styles.bzoSubSection}>
|
||||||
|
<h5 className={styles.bzoSubTitle}>Gemeinde</h5>
|
||||||
|
<div className={styles.bzoInfoGrid}>
|
||||||
|
<div className={styles.bzoInfoItem}>
|
||||||
|
<span className={styles.bzoLabel}>Name:</span>
|
||||||
|
<span className={styles.bzoValue}>{data.gemeinde.label}</span>
|
||||||
|
</div>
|
||||||
|
{data.gemeinde.plz && (
|
||||||
|
<div className={styles.bzoInfoItem}>
|
||||||
|
<span className={styles.bzoLabel}>PLZ:</span>
|
||||||
|
<span className={styles.bzoValue}>{data.gemeinde.plz}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Zones */}
|
||||||
|
{data.extracted_content?.zones && data.extracted_content.zones.length > 0 && (
|
||||||
|
<div className={styles.bzoSubSection}>
|
||||||
|
<h5 className={styles.bzoSubTitle}>
|
||||||
|
Zonen ({data.extracted_content.zones.length})
|
||||||
|
</h5>
|
||||||
|
<div className={styles.bzoZonesList}>
|
||||||
|
{data.extracted_content.zones.map((zone, idx) => (
|
||||||
|
<div key={idx} className={styles.bzoZoneCard}>
|
||||||
|
<div className={styles.bzoZoneHeader}>
|
||||||
|
<span className={styles.bzoZoneCode}>{zone.zone_code}</span>
|
||||||
|
<span className={styles.bzoZoneName}>{zone.zone_name}</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.bzoZoneDetails}>
|
||||||
|
<div className={styles.bzoZoneDetailItem}>
|
||||||
|
<span className={styles.bzoDetailLabel}>Kategorie:</span>
|
||||||
|
<span className={styles.bzoDetailValue}>{zone.zone_category}</span>
|
||||||
|
</div>
|
||||||
|
{zone.geschosszahl !== undefined && (
|
||||||
|
<div className={styles.bzoZoneDetailItem}>
|
||||||
|
<span className={styles.bzoDetailLabel}>Geschosszahl:</span>
|
||||||
|
<span className={styles.bzoDetailValue}>{zone.geschosszahl}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={styles.bzoZoneDetailItem}>
|
||||||
|
<span className={styles.bzoDetailLabel}>Gewerbeerleichterung:</span>
|
||||||
|
<span className={styles.bzoDetailValue}>
|
||||||
|
{zone.gewerbeerleichterung ? 'Ja' : 'Nein'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{zone.source_article && (
|
||||||
|
<div className={styles.bzoZoneDetailItem}>
|
||||||
|
<span className={styles.bzoDetailLabel}>Quelle:</span>
|
||||||
|
<span className={styles.bzoDetailValue}>
|
||||||
|
{zone.source_article} (Seite {zone.page})
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Relevant Rules */}
|
||||||
|
{data.relevant_rules && data.relevant_rules.length > 0 && (
|
||||||
|
<div className={styles.bzoSubSection}>
|
||||||
|
<h5 className={styles.bzoSubTitle}>
|
||||||
|
Relevante Regeln ({data.relevant_rules.length})
|
||||||
|
</h5>
|
||||||
|
<div className={styles.bzoRulesList}>
|
||||||
|
{data.relevant_rules.map((rule, idx) => (
|
||||||
|
<div key={idx} className={styles.bzoRuleCard}>
|
||||||
|
<div className={styles.bzoRuleHeader}>
|
||||||
|
<span className={styles.bzoRuleType}>{rule.rule_type}</span>
|
||||||
|
{rule.confidence && (
|
||||||
|
<span className={styles.bzoConfidence}>
|
||||||
|
{Math.round(rule.confidence * 100)}% Vertrauen
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={styles.bzoRuleValue}>
|
||||||
|
{rule.value_numeric !== undefined && rule.unit ? (
|
||||||
|
<span className={styles.bzoRuleNumeric}>
|
||||||
|
{rule.value_numeric} {rule.unit}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className={styles.bzoRuleText}>{rule.value_text}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{rule.zone_raw && (
|
||||||
|
<div className={styles.bzoRuleZone}>
|
||||||
|
Zone: {rule.zone_raw}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{rule.page && (
|
||||||
|
<div className={styles.bzoRulePage}>
|
||||||
|
Seite {rule.page}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* All Rules */}
|
||||||
|
{data.extracted_content?.rules && data.extracted_content.rules.length > 0 && (
|
||||||
|
<div className={styles.bzoSubSection}>
|
||||||
|
<h5 className={styles.bzoSubTitle}>
|
||||||
|
Alle Regeln ({data.extracted_content.rules.length})
|
||||||
|
</h5>
|
||||||
|
<div className={styles.bzoRulesList}>
|
||||||
|
{data.extracted_content.rules.map((rule, idx) => (
|
||||||
|
<div key={idx} className={styles.bzoRuleCard}>
|
||||||
|
<div className={styles.bzoRuleHeader}>
|
||||||
|
<span className={styles.bzoRuleType}>{rule.rule_type}</span>
|
||||||
|
{rule.confidence && (
|
||||||
|
<span className={styles.bzoConfidence}>
|
||||||
|
{Math.round(rule.confidence * 100)}%
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={styles.bzoRuleValue}>
|
||||||
|
{rule.value_numeric !== undefined && rule.unit ? (
|
||||||
|
<span className={styles.bzoRuleNumeric}>
|
||||||
|
{rule.value_numeric} {rule.unit}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className={styles.bzoRuleText}>{rule.value_text}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{rule.text_snippet && (
|
||||||
|
<div className={styles.bzoRuleSnippet}>
|
||||||
|
"{rule.text_snippet}"
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={styles.bzoRuleMeta}>
|
||||||
|
{rule.zone_raw && <span>Zone: {rule.zone_raw}</span>}
|
||||||
|
{rule.page && <span>Seite {rule.page}</span>}
|
||||||
|
{rule.rule_scope && <span>Bereich: {rule.rule_scope}</span>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Articles */}
|
||||||
|
{data.extracted_content?.articles && data.extracted_content.articles.length > 0 && (
|
||||||
|
<div className={styles.bzoSubSection}>
|
||||||
|
<h5 className={styles.bzoSubTitle}>
|
||||||
|
Artikel ({data.extracted_content.articles.length})
|
||||||
|
</h5>
|
||||||
|
<div className={styles.bzoArticlesList}>
|
||||||
|
{data.extracted_content.articles.map((article, idx) => (
|
||||||
|
<div key={idx} className={styles.bzoArticleCard}>
|
||||||
|
<div className={styles.bzoArticleHeader}>
|
||||||
|
<span className={styles.bzoArticleLabel}>{article.article_label}</span>
|
||||||
|
<span className={styles.bzoArticleTitle}>{article.article_title}</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.bzoArticleText}>
|
||||||
|
<ReactMarkdown
|
||||||
|
remarkPlugins={[remarkGfm]}
|
||||||
|
className={styles.bzoMarkdown}
|
||||||
|
components={{
|
||||||
|
h1: ({node, ...props}) => <h1 className={styles.bzoMarkdownH1} {...props} />,
|
||||||
|
h2: ({node, ...props}) => <h2 className={styles.bzoMarkdownH2} {...props} />,
|
||||||
|
h3: ({node, ...props}) => <h3 className={styles.bzoMarkdownH3} {...props} />,
|
||||||
|
h4: ({node, ...props}) => <h4 className={styles.bzoMarkdownH4} {...props} />,
|
||||||
|
h5: ({node, ...props}) => <h5 className={styles.bzoMarkdownH5} {...props} />,
|
||||||
|
h6: ({node, ...props}) => <h6 className={styles.bzoMarkdownH6} {...props} />,
|
||||||
|
p: ({node, ...props}) => <p className={styles.bzoMarkdownP} {...props} />,
|
||||||
|
ul: ({node, ...props}) => <ul className={styles.bzoMarkdownUl} {...props} />,
|
||||||
|
ol: ({node, ...props}) => <ol className={styles.bzoMarkdownOl} {...props} />,
|
||||||
|
li: ({node, ...props}) => <li className={styles.bzoMarkdownLi} {...props} />,
|
||||||
|
table: ({node, ...props}) => <div className={styles.bzoMarkdownTableWrapper}><table className={styles.bzoMarkdownTable} {...props} /></div>,
|
||||||
|
thead: ({node, ...props}) => <thead className={styles.bzoMarkdownThead} {...props} />,
|
||||||
|
tbody: ({node, ...props}) => <tbody className={styles.bzoMarkdownTbody} {...props} />,
|
||||||
|
tr: ({node, ...props}) => <tr className={styles.bzoMarkdownTr} {...props} />,
|
||||||
|
th: ({node, ...props}) => <th className={styles.bzoMarkdownTh} {...props} />,
|
||||||
|
td: ({node, ...props}) => <td className={styles.bzoMarkdownTd} {...props} />,
|
||||||
|
code: ({node, inline, ...props}: any) =>
|
||||||
|
inline ? (
|
||||||
|
<code className={styles.bzoMarkdownCodeInline} {...props} />
|
||||||
|
) : (
|
||||||
|
<code className={styles.bzoMarkdownCodeBlock} {...props} />
|
||||||
|
),
|
||||||
|
pre: ({node, ...props}) => <pre className={styles.bzoMarkdownPre} {...props} />,
|
||||||
|
blockquote: ({node, ...props}) => <blockquote className={styles.bzoMarkdownBlockquote} {...props} />,
|
||||||
|
strong: ({node, ...props}) => <strong className={styles.bzoMarkdownStrong} {...props} />,
|
||||||
|
em: ({node, ...props}) => <em className={styles.bzoMarkdownEm} {...props} />,
|
||||||
|
a: ({node, ...props}: any) => <a className={styles.bzoMarkdownLink} {...props} />,
|
||||||
|
hr: ({node, ...props}) => <hr className={styles.bzoMarkdownHr} {...props} />,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{article.text}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
<div className={styles.bzoArticleMeta}>
|
||||||
|
{article.zone_raw && <span>Zone: {article.zone_raw}</span>}
|
||||||
|
<span>Seiten {article.page_start}-{article.page_end}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Documents Processed */}
|
||||||
|
{data.documents_processed && data.documents_processed.length > 0 && (
|
||||||
|
<div className={styles.bzoSubSection}>
|
||||||
|
<h5 className={styles.bzoSubTitle}>
|
||||||
|
Verarbeitete Dokumente ({data.documents_processed.length})
|
||||||
|
</h5>
|
||||||
|
<div className={styles.bzoDocumentsList}>
|
||||||
|
{data.documents_processed.map((doc, idx) => (
|
||||||
|
<div key={idx} className={styles.bzoDocumentItem}>
|
||||||
|
<span className={styles.bzoDocumentLabel}>{doc.label}</span>
|
||||||
|
<span className={styles.bzoDocumentType}>{doc.dokumentTyp}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Errors and Warnings */}
|
||||||
|
{(data.errors?.length > 0 || data.warnings?.length > 0) && (
|
||||||
|
<div className={styles.bzoSubSection}>
|
||||||
|
{(data.errors?.length > 0) && (
|
||||||
|
<div className={styles.bzoErrors}>
|
||||||
|
<h5 className={styles.bzoErrorTitle}>Fehler ({data.errors.length})</h5>
|
||||||
|
<ul className={styles.bzoErrorList}>
|
||||||
|
{data.errors.map((error, idx) => (
|
||||||
|
<li key={idx}>{error}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{(data.warnings?.length > 0) && (
|
||||||
|
<div className={styles.bzoWarnings}>
|
||||||
|
<h5 className={styles.bzoWarningTitle}>Warnungen ({data.warnings.length})</h5>
|
||||||
|
<ul className={styles.bzoWarningList}>
|
||||||
|
{data.warnings.map((warning, idx) => (
|
||||||
|
<li key={idx}>{warning}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Statistics */}
|
||||||
|
{data.extracted_content && (
|
||||||
|
<div className={styles.bzoSubSection}>
|
||||||
|
<h5 className={styles.bzoSubTitle}>Statistiken</h5>
|
||||||
|
<div className={styles.bzoStats}>
|
||||||
|
<div className={styles.bzoStatItem}>
|
||||||
|
<span className={styles.bzoStatLabel}>Zonen:</span>
|
||||||
|
<span className={styles.bzoStatValue}>{data.extracted_content.total_zones}</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.bzoStatItem}>
|
||||||
|
<span className={styles.bzoStatLabel}>Regeln:</span>
|
||||||
|
<span className={styles.bzoStatValue}>{data.extracted_content.total_rules}</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.bzoStatItem}>
|
||||||
|
<span className={styles.bzoStatLabel}>Artikel:</span>
|
||||||
|
<span className={styles.bzoStatValue}>{data.extracted_content.total_articles}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,19 @@ export interface ParcelSearchResponse {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
|
gemeinde?: {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
plz: string;
|
||||||
|
};
|
||||||
|
documents?: Array<{
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
dokumentTyp: string;
|
||||||
|
dokumentReferenz: string;
|
||||||
|
quelle: string;
|
||||||
|
mimeType: string;
|
||||||
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command response interface
|
// Command response interface
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue