for merge

This commit is contained in:
djeon 2025-10-29 06:01:41 +09:00
commit a0f42bb22d
314 changed files with 1382 additions and 2185 deletions

View File

@ -650,6 +650,7 @@ code + .copy-button {
<script type="text/javascript">
function configurationCacheProblems() { return (
// begin-report-data
{"diagnostics":[{"locations":[{"path":"/Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesSectionEntity.java"},{"taskPath":":meeting:compileJava"}],"problem":[{"text":"/Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesSectionEntity.java uses or overrides a deprecated API."}],"severity":"ADVICE","problemDetails":[{"text":"Note: /Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesSectionEntity.java uses or overrides a deprecated API."}],"contextualLabel":"/Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesSectionEntity.java uses or overrides a deprecated API.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.deprecated.filename","displayName":"/Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesSectionEntity.java uses or overrides a deprecated API."}]},{"locations":[{"path":"/Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesSectionEntity.java"},{"taskPath":":meeting:compileJava"}],"problem":[{"text":"Recompile with -Xlint:deprecation for details."}],"severity":"ADVICE","problemDetails":[{"text":"Note: Recompile with -Xlint:deprecation for details."}],"contextualLabel":"Recompile with -Xlint:deprecation for details.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.deprecated.recompile","displayName":"Recompile with -Xlint:deprecation for details."}]}],"problemsReport":{"totalProblemCount":2,"buildName":"hgzero","requestedTasks":":meeting:bootRun","documentationLink":"https://docs.gradle.org/8.14/userguide/reporting_problems.html","documentationLinkCaption":"Problem report","summaries":[]}}
{"diagnostics":[{"locations":[{"path":"/Users/daewoong/home/workspace/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/request/InviteParticipantRequest.java"},{"taskPath":":meeting:compileJava"}],"problem":[{"text":"Some input files use or override a deprecated API."}],"severity":"ADVICE","problemDetails":[{"text":"Note: Some input files use or override a deprecated API."}],"contextualLabel":"Some input files use or override a deprecated API.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.deprecated.plural","displayName":"Some input files use or override a deprecated API."}]},{"locations":[{"path":"/Users/daewoong/home/workspace/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/request/InviteParticipantRequest.java"},{"taskPath":":meeting:compileJava"}],"problem":[{"text":"Recompile with -Xlint:deprecation for details."}],"severity":"ADVICE","problemDetails":[{"text":"Note: Recompile with -Xlint:deprecation for details."}],"contextualLabel":"Recompile with -Xlint:deprecation for details.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.deprecated.recompile","displayName":"Recompile with -Xlint:deprecation for details."}]}],"problemsReport":{"totalProblemCount":2,"buildName":"hgzero","requestedTasks":":meeting:bootRun","documentationLink":"https://docs.gradle.org/8.14/userguide/reporting_problems.html","documentationLinkCaption":"Problem report","summaries":[]}}
// end-report-data
);}

View File

@ -715,13 +715,22 @@
const meetingId = card.dataset.id;
const meetingStatus = card.dataset.status;
// 회의 정보 조회
const meeting = SAMPLE_MEETINGS.find(m => m.id == meetingId);
const isCreator = meeting.participants.some(p => p.id === currentUser.id && p.role === 'creator');
// 상태에 따른 이동 처리
if (meetingStatus === 'ongoing') {
navigateTo('05-회의진행.html');
} else if (meetingStatus === 'draft' || meetingStatus === 'complete' || meetingStatus === 'completed') {
navigateTo('10-회의록상세조회.html');
} else if (meetingStatus === 'scheduled') {
navigateTo('03-회의예약.html');
// 생성자: 회의예약 화면(수정), 참여자: 시스템 알럿
if (isCreator) {
navigateTo('03-회의예약.html');
} else {
alert('아직 회의 시간이 되지 않아 참여하실 수 없습니다');
}
}
}
});
@ -782,7 +791,7 @@
// 예정된 회의 개수 (예정 + 진행중)
const scheduled = SAMPLE_MEETINGS.filter(m => m.status === 'scheduled' || m.status === 'ongoing').length;
// 작성중 회의록 개수 (내가 참한 회의 중 '작성중' 상태)
// 작성중 회의록 개수 (내가 참한 회의 중 '작성중' 상태)
const drafts = SAMPLE_MINUTES.filter(m =>
m.status === 'draft' &&
m.participants.some(p => p.id === currentUser.id)

View File

@ -226,7 +226,7 @@
transform: translateX(22px);
}
/* 참자 */
/* 참자 */
.participants-chips {
display: flex;
flex-wrap: wrap;
@ -420,18 +420,18 @@
</div>
</section>
<!---->
<!---->
<section class="form-section">
<h2 class="section-title">자 *</h2>
<h2 class="section-title">자 *</h2>
<!--자 칩 -->
<!--자 칩 -->
<div class="participants-chips" id="participants-chips">
<!-- 동적으로 생성 -->
</div>
<!--자 추가 버튼 -->
<!--자 추가 버튼 -->
<button type="button" class="add-participant-btn" id="add-participant-btn">
자 추가
자 추가
</button>
</section>
@ -459,11 +459,11 @@
<button type="submit" form="meeting-form" class="btn btn-primary">예약 완료</button>
</div>
<!--자 추가 모달 -->
<!--자 추가 모달 -->
<div class="modal-overlay" id="add-participant-modal">
<div class="modal">
<div class="modal-header">
<h3 class="modal-title">자 추가</h3>
<h3 class="modal-title">자 추가</h3>
<button class="modal-close"></button>
</div>
<div class="modal-body">
@ -504,7 +504,7 @@
avatarColor: storedUser.avatarColor || CURRENT_USER.avatarColor
} : CURRENT_USER;
// 참자 목록 (현재 사용자는 기본 포함)
// 참자 목록 (현재 사용자는 기본 포함)
let participants = [currentUser];
// 폼 요소
@ -670,7 +670,7 @@
}
/**
* 참자 칩 렌더링
* 참자 칩 렌더링
*/
function renderParticipantChips() {
participantsChipsContainer.innerHTML = participants.map(p => `
@ -693,10 +693,10 @@
}
/**
* 참자 검색 결과 렌더링
* 참자 검색 결과 렌더링
*/
function renderSearchResults(query) {
// 샘플 사용자 목록에서 이미 추가된 참자 제외
// 샘플 사용자 목록에서 이미 추가된 참자 제외
const availableUsers = SAMPLE_USERS.filter(u =>
!participants.some(participant => participant.id === u.id)
);
@ -761,7 +761,7 @@
}
if (participants.length === 0) {
showToast('최소 1명의 참자를 추가해주세요', 'error');
showToast('최소 1명의 참자를 추가해주세요', 'error');
return false;
}
@ -855,13 +855,13 @@
showToast('회의 링크가 생성되었습니다', 'success');
});
// 참자 추가
// 참자 추가
addParticipantBtn.addEventListener('click', () => {
openModal('add-participant-modal');
renderSearchResults('');
});
// 참자 검색
// 참자 검색
participantSearch.addEventListener('input', () => {
renderSearchResults(participantSearch.value);
});

View File

@ -294,7 +294,7 @@
overflow-wrap: break-word;
}
/* 참자 탭 */
/* 참자 탭 */
.participant-section-header {
display: flex;
justify-content: space-between;
@ -679,7 +679,7 @@
<div class="tabs-container">
<div class="tabs-header">
<button class="tab-button active" onclick="switchTab('participants')">
</button>
<button class="tab-button" onclick="switchTab('ai-suggestions')">
AI 메모
@ -692,10 +692,10 @@
</button>
</div>
<!--자 탭 -->
<!--자 탭 -->
<div class="tab-content active" id="tab-participants">
<div class="participant-section-header">
<h3 class="participant-section-title">자 관리</h3>
<h3 class="participant-section-title">자 관리</h3>
<span class="participant-count">총 4명</span>
</div>
@ -1043,7 +1043,7 @@
document.getElementById(`tab-${tabName}`).classList.add('active');
}
// 참자 초대
// 참자 초대
function inviteParticipant() {
const emailInput = document.getElementById('inviteEmail');
const email = emailInput.value.trim();

View File

@ -272,7 +272,7 @@
<div class="stats-grid">
<div class="stat-item">
<div class="stat-value" id="participantsValue">4명</div>
<div class="stat-label"></div>
<div class="stat-label"></div>
</div>
<div class="stat-item">
<div class="stat-value" id="durationValue">90분</div>
@ -571,7 +571,7 @@
// 바로 최종 확정
function confirmMeetingDirectly() {
if (confirm('AI가 정리한 내용 그대로 최종 확정하시겠습니까?\n\n모든 안건이 자동으로 검증 완료 처리되며, 참자에게 확정 알림이 발송됩니다.')) {
if (confirm('AI가 정리한 내용 그대로 최종 확정하시겠습니까?\n\n모든 안건이 자동으로 검증 완료 처리되며, 참자에게 확정 알림이 발송됩니다.')) {
showToast('회의록이 최종 확정되었습니다', 'success');
setTimeout(() => {
navigateTo('02-대시보드.html');

View File

@ -1083,7 +1083,7 @@
<div class="stats-grid">
<div class="stat-item">
<div class="stat-value">4명</div>
<div class="stat-label"></div>
<div class="stat-label"></div>
</div>
<div class="stat-item">
<div class="stat-value">90분</div>

View File

@ -246,7 +246,7 @@
}
.agenda-verification:not([data-is-creator="true"]) .creator-only {
display: none; /* 참자 환경에서는 숨김 */
display: none; /* 참자 환경에서는 숨김 */
}
.agenda-verification:not(.verified) .creator-only {
@ -339,11 +339,11 @@
10-회의록상세조회.html → 하단 액션 바 "수정" 버튼 클릭
[권한 제어]
- 검증완료 전 (작성중/초안 상태): 모든 참자가 수정 가능
- 검증완료 후: 회의 생성자만 수정 가능 (참자는 "수정" 버튼 비활성화)
- 참자 관리: 회의 생성자만 추가/삭제 가능
- 검증완료 전 (작성중/초안 상태): 모든 참자가 수정 가능
- 검증완료 후: 회의 생성자만 수정 가능 (참자는 "수정" 버튼 비활성화)
- 참자 관리: 회의 생성자만 추가/삭제 가능
- 회의 일시/장소: 읽기 전용 (회의 예약 화면에서만 변경 가능)
- 안건별 검증: 회의 생성자는 잠금 해제 후 수정 가능, 참자는 읽기 전용
- 안건별 검증: 회의 생성자는 잠금 해제 후 수정 가능, 참자는 읽기 전용
- AI 한줄 요약: 모든 사용자에게 읽기 전용 (UFR-AI-036)
-->
@ -469,7 +469,7 @@
</div>
<!-- 안건별 검증 (UFR-COLLAB-030) -->
<!--자: 체크박스만 표시 (검증완료 시 읽기 전용) -->
<!--자: 체크박스만 표시 (검증완료 시 읽기 전용) -->
<!-- 생성자: 검증완료 시 "잠금 해제" 버튼 표시 -->
<div class="agenda-verification verified" id="verify-agenda-1" data-is-creator="true">
<input type="checkbox" class="checkbox" id="verify-1" checked disabled>
@ -546,7 +546,7 @@
</div>
<!-- 안건별 검증 (UFR-COLLAB-030) -->
<!--자: 체크박스만 표시 (검증완료 시 읽기 전용) -->
<!--자: 체크박스만 표시 (검증완료 시 읽기 전용) -->
<!-- 생성자: 검증완료 시 "잠금 해제" 버튼 표시 -->
<div class="agenda-verification verified" id="verify-agenda-2" data-is-creator="true">
<input type="checkbox" class="checkbox" id="verify-2" checked disabled>

View File

@ -513,7 +513,7 @@
<!-- 참여 유형 탭 (다중 선택 가능) -->
<div class="participation-tabs">
<button class="participation-tab" data-type="attended" onclick="toggleParticipationType('attended')">
한 회의
한 회의
</button>
<button class="participation-tab" data-type="created" onclick="toggleParticipationType('created')">
생성한 회의
@ -527,7 +527,7 @@
<input
type="text"
class="search-input"
placeholder="회의 제목, 참자, 키워드 검색"
placeholder="회의 제목, 참자, 키워드 검색"
id="searchInput"
oninput="applyFilters()"
onkeypress="if(event.key==='Enter') handleSearch()"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,135 +0,0 @@
spring:
application:
name: meeting
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
# Database Configuration
datasource:
url: jdbc:${DB_KIND:postgresql}://${DB_HOST:4.230.48.72}:${DB_PORT:5432}/${DB_NAME:meetingdb}
username: ${DB_USERNAME:hgzerouser}
password: ${DB_PASSWORD:Hi5Jessica!}
driver-class-name: org.postgresql.Driver
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
leak-detection-threshold: 60000
# JPA Configuration
jpa:
show-sql: ${SHOW_SQL:true}
database-platform: org.hibernate.dialect.PostgreSQLDialect
properties:
hibernate:
format_sql: true
use_sql_comments: true
dialect: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: ${JPA_DDL_AUTO:update}
# Redis Configuration
data:
redis:
host: ${REDIS_HOST:20.249.177.114}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:Hi5Jessica!}
timeout: 2000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
database: ${REDIS_DATABASE:1}
# Server Configuration
server:
port: ${SERVER_PORT:8082}
# JWT Configuration
jwt:
secret: ${JWT_SECRET:hgzero-jwt-secret-key-for-dev-environment-only-do-not-use-in-production-minimum-256-bits}
access-token-validity: ${JWT_ACCESS_TOKEN_VALIDITY:3600}
refresh-token-validity: ${JWT_REFRESH_TOKEN_VALIDITY:604800}
# CORS Configuration
cors:
allowed-origins: ${CORS_ALLOWED_ORIGINS:http://localhost:*}
# Actuator Configuration
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
base-path: /actuator
endpoint:
health:
show-details: always
show-components: always
health:
livenessState:
enabled: true
readinessState:
enabled: true
# OpenAPI Documentation
springdoc:
api-docs:
path: /v3/api-docs
swagger-ui:
path: /swagger-ui.html
tags-sorter: alpha
operations-sorter: alpha
show-actuator: false
# Logging Configuration
logging:
level:
root: ${LOG_LEVEL_ROOT:INFO}
com.unicorn.hgzero.meeting: ${LOG_LEVEL_APP:DEBUG}
org.springframework.web: ${LOG_LEVEL_WEB:INFO}
org.springframework.security: ${LOG_LEVEL_SECURITY:DEBUG}
org.springframework.websocket: ${LOG_LEVEL_WEBSOCKET:DEBUG}
org.hibernate.SQL: ${LOG_LEVEL_SQL:DEBUG}
org.hibernate.type: ${LOG_LEVEL_SQL_TYPE:TRACE}
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file:
name: ${LOG_FILE:logs/meeting-service.log}
logback:
rollingpolicy:
max-file-size: ${LOG_MAX_FILE_SIZE:10MB}
max-history: ${LOG_MAX_HISTORY:7}
total-size-cap: ${LOG_TOTAL_SIZE_CAP:100MB}
# External API Configuration
api:
claude:
key: ${CLAUDE_API_KEY:}
url: ${CLAUDE_API_URL:https://api.anthropic.com}
openai:
key: ${OPENAI_API_KEY:}
url: ${OPENAI_API_URL:https://api.openai.com}
openweather:
key: ${OPENWEATHER_API_KEY:}
url: ${OPENWEATHER_API_URL:https://api.openweathermap.org}
kakao:
key: ${KAKAO_API_KEY:}
url: ${KAKAO_API_URL:https://dapi.kakao.com}
# Azure EventHub Configuration
eventhub:
connection-string: ${EVENTHUB_CONNECTION_STRING:}
name: ${EVENTHUB_NAME:hgzero-events}
consumer-group: ${EVENTHUB_CONSUMER_GROUP:$Default}
# Azure Storage Configuration (for EventHub checkpoints)
azure:
storage:
connection-string: ${AZURE_STORAGE_CONNECTION_STRING:}
container: ${AZURE_STORAGE_CONTAINER:hgzero-checkpoints}

Some files were not shown because too many files have changed in this diff Show More