기존엔 미리보기가 큰 타일 하나만 바꾸고, 배경/카드 등 화면 전체는 실제
장착된 테마를 따로 불러와 보여줬다. 이제는 _resolvePreviewTemplate()
하나로 "지금 보여줄 테마"를 통일해서, 체험하기로 고른 템플릿이 배경색/
보유 캐시 바/카드 전부에 즉시 반영된다. "기본 모습"을 고르면 화면 전체가
원래 모습으로 돌아간다.
ShopTemplate에 code 필드를 추가해서 profileThemes 맵을 바로 찾아 쓸 수
있게 했다.
1. 커스텀(상점) 화면 자체도 장착한 테마를 반영하도록 함: 배경색, 보유 캐시
바 배경, 템플릿 카드 배경/테두리/텍스트, 체험하기·장착하기 버튼 색까지
전부 테마를 따라간다. 미리보기는 기존처럼 큰 타일 하나만 별도로 바뀐다.
2. 메인 대시보드의 테마 반영 대상을 학생+관리자에서 학생+교사+관리자로
확장 - 백엔드에서 교사 계정도 템플릿을 무료로 쓸 수 있게 된 것과 짝.
같은 _tileSpecs/_buildModernCard 코드를 역할 구분 없이 공유하므로
테마 로딩 조건만 넓히면 교사/관리자 타일에도 자동으로 적용된다.
용어 정리: 템플릿 = 상점에서 사는 색/애니메이션 상품, 테마 = 그 템플릿이
몰고 오는 화면 전체 디자인(배경색/타일색/아이콘색). 1단계로 메인 대시보드부터
적용 - 프로필 카드 그라데이션뿐 아니라 페이지 배경, 타일 카드 배경/테두리,
아이콘 동그라미 배경, 타일 아이콘 강조색, 제목/부제목 텍스트 색까지 장착한
템플릿에 맞춰 통째로 바뀐다. 잠긴/비활성 타일(NFC 미인증 등)은 기존처럼
중립 회색(sage)을 그대로 써서 상태 구분은 유지.
6개 기존 템플릿마다 직접 디자인한 테마(lib/theme/profile_themes.dart)를
새로 만들었고, 최고가 "갤럭시" 템플릿은 다른 5개와 달리 통째로 다크 테마로
디자인해 프리미엄 아이템 느낌을 살렸다.
백엔드 /api/shop/equipped가 템플릿 code를 같이 내려주도록 수정해서
프론트엔드가 code로 디자인해둔 테마를 찾아 쓸 수 있게 했다.
- 프로필 꾸미기: 미리보기 중인 큰 타일의 테두리도 테마 색으로 바뀌게 수정,
미구매 템플릿에 "체험하기" 버튼을 따로 둬서 사기 전에도 눌러볼 수 있게 함
- 앱 전반의 확인/삭제/저장 같은 다이얼로그 기본 액션 버튼에 autofocus를 줘서
엔터 키로도 눌리게 함 (텍스트 입력이 있는 다이얼로그는 입력창 쪽에
autofocus + onSubmitted를 달아 입력 중 바로 엔터로 제출되게 처리)
- HoldToRevealPasswordField 위젯 추가 - 눈 아이콘을 누르고 있는 동안만
비밀번호가 평문으로 보임. 로그인/간편인증/초기 비밀번호 변경 화면에 적용
템플릿 카드를 누르면(구매 여부 상관없이) 위쪽 "기본 프로필 꾸미기" 큰 타일이
그 테마 그라데이션으로 바로 바뀌어서 사기 전에 실제 느낌을 볼 수 있다.
미리보기 중인 카드에는 "미리보기" 표시와 테두리로 구분을 주고, 아무것도
직접 고르지 않았을 땐 실제 장착된 테마를 기본으로 보여준다.
멈춰있으면 시작, 돌아가는 중이면 일시정지, 일시정지 중이면 재개로
스페이스바 한 키로 토글되게 했다. 뽀모도로가 공부/휴식을 자동으로
전환하는 중에는 화면에서도 수동 버튼을 숨기므로 스페이스바도 똑같이
막아서 자동 전환을 방해하지 않게 한다. 뽀모도로 설정 모달을 닫으면
포커스를 다시 돌려줘서 스페이스바가 계속 먹히게 처리.
카테고리 타일 내부 패딩/간격을 줄이고 제목·부제목에 ellipsis를 줘서
"준비중" 텍스트가 타일 밖으로 삐져나오던 문제를 해결. 관리자 계정도
백엔드에서 템플릿을 무료로 쓸 수 있게 된 것에 맞춰, 메인 대시보드
프로필 카드의 장착 테마 반영을 관리자 계정에도 적용한다.
ListView(스크롤 영역) 안에서 Row에 stretch 정렬만 주면 높이가 정해지지 않아
자식 크기 계산이 깨지는 문제였다. 카테고리 타일 Row 전체를 SizedBox로
감싸 높이를 고정하니 "기본 프로필 꾸미기" 타일과 그 아래 템플릿 목록이
정상적으로 그려진다.
백엔드에 새로 생긴 /api/shop/* 엔드포인트를 붙여서 프로필 커스텀 화면을
실제 상점으로 채웠다. 랭킹/친구창 배경, 출석/그룹창 표시 카테고리는
와이어프레임 자리만 잡아둔 준비중 상태 - 1단계는 기본 프로필 테마만 동작.
장착한 테마는 메인 대시보드 프로필 카드 배경에 바로 반영된다.
학생이 직접 템플릿을 만들어 파는 기능은 이후 단계로 보류.
danger/warning/success/approved(+각 Bg 버전)를 AppPalette에 추가하고,
화면마다 다르게 쓰이던 Colors.red[400/600/700]/redAccent, orange/amber
계열, blue 계열 리터럴을 전부 해당 토큰으로 교체. NFC 체크인 화면의
홀로 튀던 Colors.blueAccent도 함께 정리.
메인 대시보드 프로필 카드 오른쪽 위에 작은 편집(연필) 버튼을 달아서
누르면 새로 만든 빈 커스텀 화면(ProfileCustomizeScreen)으로 이동한다.
아직은 자리만 잡아둔 화면이고, 실제 커스텀 기능들은 이후에 하나씩 추가.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
가로로 길게 늘어지던 검색창을 위쪽 "신규 학생 계정 추가" 타일과
같은 폭(formSquareSize)으로 맞추고, 좁은 화면에서 필터 칩들이
잘리지 않게 Row 대신 Wrap으로 변경.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
오늘 공부 시간이 30분씩 늘어날 때마다 프로필 원형 테두리가
흰→노랑→주황→빨강→보라 순으로 진해지도록(2시간 이상은 보라 고정)
꾸며서 누가 얼마나 공부하고 있는지 한눈에 보이게 함. 범례도 추가.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
교사가 앱에 직접 접속해서 위치를 바꾸는 대신, 교무실 출입구의
Stream Deck(15키)에서 이름 버튼 하나로 "수업 중"을 표시하고,
설정 버튼으로 과학실/출장 등 다른 위치도 고를 수 있게 하는 라즈베리파이용
독립 스크립트. 기존 POST /api/teacher-call/location API를 그대로
호출하므로 서버/앱 변경 없이 별도 기기에서 실행하면 된다. 선생님
목록은 /api/teacher-call/teachers에서 1분마다 자동 새로고침해서
새로 가입한 교사도 재시작 없이 반영된다.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
브라우저 탭 자체는 그대로 두고 앱 안에서 다른 화면으로 이동하면
AppLifecycleState가 안 바뀌어서 기존 "탭 이탈" 감지가 못 잡는 사각
지대였음. 화면 dispose 시점에 타이머가 running이면 직접 알림을 띄움.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
넓은 화면(웹 + 900px 이상)에서는 타일이 한 줄에 더 많이 들어가서
관리자 계정이 아니면 스크롤 자체가 거의 필요 없어지게 함(스크롤
바운스로 생기던 파란 띠 회피). 모바일/앱에서는 기존 4칸 유지.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
firebase.json의 no-cache 규칙이 "/index.html" 경로에만 걸려있었는데,
실제로 브라우저는 "/"를 요청하고 SPA rewrite로 index.html을 받다보니
그 규칙이 안 먹혀서 1시간 동안 캐시됐다. 이 때문에 배포 직후에도
최대 1시간까지 이전 버전(스크롤 바운스 파란색 버그 등)이 그대로
보이는 문제가 있었음. "/" 경로에도 같은 no-cache 규칙을 추가.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
"custom numFmtId starts at 164 but found a value of 0" 에러 -
구글시트/WPS 등에서 내보낸 일부 xlsx 파일은 styles.xml에 커스텀
숫자서식 id를 164 미만으로 적어두는데, excel 패키지가 이를 엄격하게
검증하며 예외를 던져 파일 전체를 못 읽었다. 파싱 전에 zip 안의
styles.xml만 열어 그 규격 위반 서식 선언을 제거하고 다시 압축해서
넘기도록 전처리 단계를 추가. 합성 xlsx로 버그 재현 + 수정 후 정상
파싱까지 직접 검증함.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
grade 쿼리파라미터를 생략하면 전체 학년 통합 랭킹을 주도록 백엔드를
바꾸고, 랭킹 화면에 "N학년"/"전체랭킹" 필 필터를 추가해 시간대
필터(오늘/이번주/전체)와 별도로 고를 수 있게 했다.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
가로로 길게 늘어지던 멤버 목록을 학생 계정관리 화면 같은 타일
그리드로 바꾸고, 오늘 공부 시간이 많은 순으로 정렬(백엔드).
화면 폭도 다른 화면들처럼 최대 560으로 제한. "백품타 바로가기"
버튼으로 그룹 화면에서 바로 공부 타이머로 이동 가능.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
그룹을 공개로 만들면 "공개 그룹" 목록에 뜨고, 학생이 참여 신청을
보내면 그룹장이 허용/거절할 수 있다. 그룹 상세 화면에 그룹장 전용
공개/비공개 전환 버튼과 대기 중인 참여 신청 목록을 추가.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
공부 타이머가 진행 중일 때 브라우저 탭이 백그라운드로 가면(다른
탭/창/앱으로 이동) 20초 뒤에도 계속 그 상태면 "다시 돌아와서
집중해볼까요?" 브라우저 알림을 띄운다. dart:html 대신 package:web +
js_interop로 구현하고, 웹이 아닌 빌드에서는 조건부 임포트로 아무
동작 안 하는 스텁을 쓴다.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
제목을 AppBar 대신 본문 상단 알약(TitlePill)으로 옮기고, 필 모양
통계/선택 버튼과 그림자 있는 둥근 타일(원형 아이콘 배지 + 코너 배지)
스타일을 출석 화면에서 그대로 가져왔다.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
별도로 돌아가던 Firebase RTDB 기반 호출 페이지(index.html/teacher.html)에서
발견한 XSS, 익명 접근(아무나 아무 선생님 위치 변경 가능), 이름 위조 문제를
우리 앱 로그인 체계로 재구현하면서 구조적으로 없앴다. 학생은 본인 계정으로만
호출하고 본인 호출 기록만 보이며, 교사는 본인 위치만 수정하고 본인이 받은
호출만 본다. TTS 대신 FCM 푸시로 대체. 시간표 데이터의 "김지"→"조신현" 오타도
"조신"으로 수정해서 옮겼다.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
브라우저가 백그라운드 탭의 타이머를 느리게 돌려서(setInterval
throttling) 카운트다운을 매 tick마다 1초씩 빼는 방식으로는 다른
창에 갔다오면 시간이 밀렸다. 남은 시간을 절대 종료 시각
(_phaseEndsAt) 기준으로 계산하도록 바꾸고, 탭이 다시 활성화될 때
(AppLifecycleState.resumed) 서버 상태를 재동기화하고 밀린 전환을
바로 따라잡도록 함.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
내가 쓰는 공부/휴식 시간 조합에 이름을 붙여 공유하고, 다른 학생이
올린 루틴을 목록에서 골라 그대로 적용할 수 있다. 인기순/최신순 정렬,
많이 쓰인 루틴일수록 사용 횟수가 올라가서 눈에 띄게 했다.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
공부/휴식 시간을 5분 단위로 직접 정해두면(기본 50/10), 공부 시간이
끝났을 때 자동으로 일시정지되고 휴식이 끝나면 자동으로 재개된다.
서버의 기존 일시정지/재개 API를 그대로 재사용하는 클라이언트 전용
스케줄러라 백엔드 변경은 없음.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
학생 참여 유도를 위해 공부 시작/종료 타이머와 학년별(오늘/이번주/전체)
랭킹 화면을 대시보드에 추가. 로그인 응답에 학년 정보를 포함시켜
세션 저장/복원 전체 경로에 전달되도록 함.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
기존엔 전체 승인이 장시간 요청도 구분 없이 다 승인해버렸음. 이제
1시간 59분 넘는 요청은 자동으로 빼고 대기중 상태로 남겨서, "많은
시간요청 학생" 목록에서 따로 검토 후 개별 승인/거절하게 함. 확인
다이얼로그에도 몇 건이 승인되고 몇 건이 제외되는지 미리 보여줌.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
기존엔 "많은 시간요청 학생" 팝업에서만 2시간 기준으로 걸러냈는데,
메인 목록 카드 자체에도 (대기중/승인/거절 등 상태 무관하게) 빨간
테두리를 씌워서 바로 눈에 띄게 함. 기준을 2시간 → 1시간 59분(119분)으로
낮춰서 두 기능이 같은 기준을 쓰도록 통일.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- "전체 학생 허용" 아이콘을 새로고침 버튼 옆에 추가 - 대기 중인 요청을
한 명씩 승인하는 대신 확인 팝업 후 한 번에 전부 승인
- "많은 시간요청 학생" 아이콘 추가 - 학생이 시간을 비정상적으로 길게
적어낼 수 있으니, 2시간 이상 신청한 대기 중인 요청만 따로 모아 보여줌
(해당 건수를 빨간 배지로 표시). 목록에서 바로 승인/거절 가능
- 컨트롤러에 durationHours/longPendingRequests/approveAllPending 추가.
일괄 승인은 서버에 batch API가 없어서 대기 요청마다 순서대로
승인 요청을 보내고 마지막에 한 번만 새로고침
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
배경색을 맞추는 대신, html/body를 position: fixed + overflow: hidden +
overscroll-behavior: none으로 화면에 못박아 브라우저가 바운스할 대상
자체가 없게 만듦 (Safari/iOS에서 rubber-band 스크롤을 막는 표준 기법).
Flutter 내부 스크롤(대시보드, 하위 화면 목록 등)은 별도로 동작하므로
영향 없음 - 실제로 정상 스크롤되는 것 확인함.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
다른 사이트/브라우저에서도 똑같이 나타난다는 걸 확인 - macOS 트랙패드의
정상적인 "탄력 바운스" 동작이었음. 다만 지난번 overflow: hidden으로
막아버린 탓에, 브라우저가 우리 페이지를 진짜 스크롤 가능한 문서로
인식하지 못해서 바운스 시 body 배경색 대신 브라우저 기본색(파란색)이
드러났던 것으로 보임. overflow: hidden을 제거해서 다른 사이트들처럼
자연스럽게 바운스되면서 body의 실제 배경색(#EEF0F2)이 드러나게 함.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
manifest.json의 theme_color/background_color가 Flutter 프로젝트
템플릿의 기본값(#0175C2, Flutter 브랜드 파란색)으로 그대로 남아있었음.
Safari가 스크롤 시 툴바 주변을 이 색으로 물들여서 파란 줄이 보인 것.
- manifest.json: theme_color를 앱의 ink색(#1C1C1C)으로, background_color를
앱 배경색(#EEF0F2)으로 교체
- index.html에도 <meta name="theme-color">를 명시적으로 추가해 이중으로 보장
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Flutter 자체 오버스크롤 글로우를 껐는데도 계속 보인다는 피드백 -
Flutter 레이어가 아니라 브라우저가 HTML 문서 자체를 스크롤 가능한
것으로 인식해서 트랙패드 제스처로 페이지 전체가 바운스되며 배경이
드러나는 것으로 보임. html/body에 overflow: hidden +
overscroll-behavior: none을 줘서 브라우저 차원의 바운스를 막고,
혹시 드러나더라도 흰색/파란색이 아니라 앱 배경색(#EEF0F2)이
보이도록 배경색도 지정.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- TitlePill을 String 기반으로 바꿔서 모든 화면(설정/교사 회원가입/관리자
시스템/NFC 태그 쓰기·체크인/학생 관리/스마트기기 반출 신청·대장/실시간
출석 현황)이 완전히 같은 폰트(굵게, 15px, 흰색)를 쓰게 통일
- 알약이 화면 맨 위에 딱 붙어 보이지 않도록 위쪽에 8px 여백 추가
- "스마트기기 반출 신청" 폼과 "관리자 시스템"의 "모든 출석 데이터 초기화"
버튼이 넓은 화면에서 양옆으로 과하게 늘어지던 문제 수정 - 최대 폭
420px로 제한하고 가운데 정렬 (기존 대비 약 1/3 크기)
- 메인 대시보드 타일 목표 크기를 184 → 276(50% 확대)로 키우고, 아이콘/
패딩/폰트 기준값도 함께 올려서 제목 10→12pt, 부제 8→10pt로 확대
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>