Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
fd26ffa
style: #54 FE1 공통 컴포넌트 CSS 구현
Aug 5, 2026
a6d049b
feat: #54 회원가입·로그인·시험기간 등록 화면 구현
Aug 5, 2026
445c1d5
feat: #54 과목 등록·가용시간 입력·시험범위 등록 화면 구현
Aug 5, 2026
fd21ed7
feat: #54 학습 작업 확인 화면 및 추가·수정 모달 구현
Aug 5, 2026
f1ae9a2
fix: #54 학습작업 수정 방식을 실제 뷰(formset 일괄저장)에 맞게 수정
Aug 5, 2026
9495610
style: #54 공통 CSS 정리 - fff 하드코딩 제거 및 화면별 스타일 통합
Aug 5, 2026
3c3b55d
fix: #54 가용시간 입력 화면 인라인 스타일 제거 및 레이아웃 정리
Aug 5, 2026
0cbeadc
fix: #54 시험범위 등록 화면 Figma 레이아웃 반영
Aug 5, 2026
17905bf
feat: #54 학습자료 상세 화면 추출·분석 자동 진행 흐름 구현
Aug 5, 2026
071841e
fix: #54 학습 작업 화면 버튼 통합 및 삭제 확인 팝업 적용
Aug 5, 2026
ae5ba8d
fix: #54 과목 등록 화면 중복 다음 버튼 제거
Aug 5, 2026
0543c34
fix: #54 템플릿 문법 오류 수정 (줄바꿈 깨짐 복구)
Aug 5, 2026
cebbb0e
Merge branch 'dev' into feature/#54-fe1-templates
Aug 5, 2026
b077121
fix: #54 학습작업 추가 화면 제목 상단 텍스트 깨짐 수정
Aug 5, 2026
82d6de9
fix: #54 가용시간 입력 화면 기존 입력값 표시(초록점) 유지되도록 수정
Aug 5, 2026
5fde07c
fix: #54 시험범위 등록 에러 표시 개선 및 추출·분석 자동 진행 로직 반영
Aug 5, 2026
5039e5e
fix: #54 학습작업 화면 좌측 과목 리스트 버튼 스타일 및 활성 표시 추가
Aug 5, 2026
11fe6bb
style: #54 회원가입·로그인 제목 크기 공통 클래스로 통일
Aug 5, 2026
b031303
style: #54 전체 제목 크기 확대 및 화면별 스타일 개선
Aug 5, 2026
f088225
fix: #54 시험기간 상세 화면 텍스트 깨짐 수정 및 전용 클래스 정리
Aug 5, 2026
4d6d4b0
style: #54 버튼·카드·인풋 스타일을 Figma 실측값 기준으로 개선
Aug 5, 2026
1f1391c
fix: 템플릿 수정
Aug 6, 2026
a380f86
Merge branch 'dev' into feature/#54-fe1-templates
Aug 6, 2026
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
40 changes: 40 additions & 0 deletions accounts/templates/accounts/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{% extends "base.html" %}
{% load static %}
{% block title %}로그인 - PLAN B{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{% static 'css/components.css' %}">
{% endblock %}

{% block content %}
<div class="auth-wrap">
<div class="card auth-card">
<div class="page-title-sm">다시 오셨네요</div>
<div class="page-sub">계획을 이어서 확인해보세요</div>

{% if form.non_field_errors %}
<div class="form-error-box">{{ form.non_field_errors|striptags }}</div>
{% endif %}

<form method="post" novalidate>
{% csrf_token %}
{% if request.GET.next %}<input type="hidden" name="next" value="{{ request.GET.next }}">{% endif %}

<div class="form-group">
<label class="form-label" for="{{ form.username.id_for_label }}">{{ form.username.label }}</label>
{{ form.username }}
</div>

<div class="form-group">
<label class="form-label" for="{{ form.password.id_for_label }}">비밀번호</label>
{{ form.password }}
</div>

<button type="submit" class="btn btn-brand btn-block">로그인</button>
</form>

<div class="auth-link">
계정이 없나요? <a href="{% url 'accounts:signup' %}">회원가입</a>
</div>
</div>
</div>
{% endblock %}
53 changes: 53 additions & 0 deletions accounts/templates/accounts/signup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{% extends "base.html" %}
{% load static %}
{% block title %}회원가입 - PLAN B{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{% static 'css/components.css' %}">
{% endblock %}

{% block content %}
<div class="auth-wrap">
<div class="card auth-card">
<div class="page-title-sm">PLAN B 시작하기</div>
<div class="page-sub">시험 계획을 함께 관리해요</div>

{% if form.non_field_errors %}
<div class="form-error-box">{{ form.non_field_errors|striptags }}</div>
{% endif %}

<form method="post" novalidate>
{% csrf_token %}

<div class="form-group">
<label class="form-label" for="{{ form.email.id_for_label }}">이메일</label>
{{ form.email }}
{% if form.email.errors %}<div class="field-error">{{ form.email.errors|striptags }}</div>{% endif %}
</div>

<div class="form-group">
<label class="form-label" for="{{ form.nickname.id_for_label }}">닉네임</label>
{{ form.nickname }}
{% if form.nickname.errors %}<div class="field-error">{{ form.nickname.errors|striptags }}</div>{% endif %}
</div>

<div class="form-group">
<label class="form-label" for="{{ form.password1.id_for_label }}">비밀번호</label>
{{ form.password1 }}
{% if form.password1.errors %}<div class="field-error">{{ form.password1.errors|striptags }}</div>{% endif %}
</div>

<div class="form-group">
<label class="form-label" for="{{ form.password2.id_for_label }}">비밀번호 확인</label>
{{ form.password2 }}
{% if form.password2.errors %}<div class="field-error">{{ form.password2.errors|striptags }}</div>{% endif %}
</div>

<button type="submit" class="btn btn-brand btn-block">회원가입</button>
</form>

<div class="auth-link">
이미 계정이 있나요? <a href="{% url 'accounts:login' %}">로그인</a>
</div>
</div>
</div>
{% endblock %}
11 changes: 11 additions & 0 deletions exams/templates/exams/_topbar.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<div style="display:flex; align-items:center; justify-content:space-between; padding:18px 0 26px;">
<a href="{% url 'exams:period_list' %}" style="font-weight:800; font-size:15px; color:var(--neutral11); text-decoration:none; letter-spacing:.03em;">
PLAN B
</a>
<form method="post" action="{% url 'accounts:logout' %}" style="margin:0;">
{% csrf_token %}
<button type="submit" class="btn btn-ghost" style="height:34px; padding:0 14px; font-size:12px;">
{{ request.user.nickname|default:request.user.username }} · 로그아웃
</button>
</form>
</div>
174 changes: 174 additions & 0 deletions exams/templates/exams/available_time_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
{% extends "base.html" %}
{% load static exam_extras %}
{% block title %}
가용시간 입력 - PLAN B
{% endblock %}

{% block extra_css %}
<link rel="stylesheet" href="{% static 'css/components.css' %}">
{% endblock %}

{% block content %}
{% include "exams/_topbar.html" %}
{% get_calendar_grid period.id as calendar_days %}

<div class="container avail-wrap">
<div class="crumb-text">
<a href="{% url 'exams:period_detail' period.id %}">PLAN B</a>
&nbsp;›&nbsp; 가용시간 입력
</div>
<div class="page-title">
가용시간 입력하기
</div>
<div class="page-sub">
하루 전체에서 공부 가능한 시간을 날짜별로 입력해주세요
</div>

{% if formset.non_form_errors %}
<div class="form-error-box">
{{ formset.non_form_errors|striptags }}
</div>
{% endif %}

<form method="post" novalidate id="available-time-form">
{% csrf_token %}
{{ formset.management_form }}

<div class="formset-hidden">
{% for form in formset %}
<div data-formset-row="{{ forloop.counter0 }}">
{{ form.id }}
{{ form.date }}
{{ form.hours }}
{{ form.minutes }}
</div>
{% endfor %}
</div>

<div class="avail-body">
<div class="card avail-cal-col">
<div class="avail-cal-title">
{{ period.start_date|date:"n월 j일" }} -
{{ period.end_date|date:"n월 j일" }}
</div>

<div class="avail-cal-grid">
<div class="avail-weekday">월</div>
<div class="avail-weekday">화</div>
<div class="avail-weekday">수</div>
<div class="avail-weekday">목</div>
<div class="avail-weekday">금</div>
<div class="avail-weekday">토</div>
<div class="avail-weekday">일</div>

{% for day in calendar_days %}
{% if forloop.first %}
{% if day.is_exam_day %}
<button type="button" class="avail-day exam-day" disabled
data-date="{{ day.date|date:'Y-m-d' }}"
style="grid-column-start:{{ day.weekday }};">
{{ day.date|date:"j" }}
</button>
{% else %}
<button type="button"
class="avail-day{% if day.has_value %} has-value{% endif %}"
data-index="{{ day.formset_index }}"
data-date="{{ day.date|date:'Y-m-d' }}"
style="grid-column-start:{{ day.weekday }};">
{{ day.date|date:"j" }}
</button>
{% endif %}
{% else %}
{% if day.is_exam_day %}
<button type="button" class="avail-day exam-day" disabled
data-date="{{ day.date|date:'Y-m-d' }}">
{{ day.date|date:"j" }}
</button>
{% else %}
<button type="button"
class="avail-day{% if day.has_value %} has-value{% endif %}"
data-index="{{ day.formset_index }}"
data-date="{{ day.date|date:'Y-m-d' }}">
{{ day.date|date:"j" }}
</button>
{% endif %}
{% endif %}
{% endfor %}
</div>
</div>

<div class="card avail-side-col disabled" id="side-panel">
<div id="side-panel-date" class="avail-side-date">
날짜를 선택하세요
</div>
<div class="avail-side-caption">선택한 날짜</div>

<div class="field-label">전체 가용 시간</div>
<input type="number" min="0" step="10"
class="form-control num"
id="side-panel-minutes"
placeholder="240">
<div class="avail-side-hint">분 단위로 입력</div>

<button type="button"
class="btn btn-brand btn-block avail-save-btn"
id="side-panel-save">
저장
</button>
</div>
</div>

<button type="submit" class="btn btn-brand avail-next-btn">
다음
</button>
</form>
</div>

<script>
(function () {
var sidePanel = document.getElementById('side-panel');
var dateLabel = document.getElementById('side-panel-date');
var minutesInput = document.getElementById('side-panel-minutes');
var saveBtn = document.getElementById('side-panel-save');
var currentDay = null;

function selectDay(dayBtn) {
document.querySelectorAll('.avail-day').forEach(function (d) {
d.classList.remove('sel');
});
dayBtn.classList.add('sel');
currentDay = dayBtn;
sidePanel.classList.remove('disabled');
dateLabel.textContent = dayBtn.dataset.date;

var row = document.querySelector(
'[data-formset-row="' + dayBtn.dataset.index + '"]'
);
var hoursInput = row.querySelector('input[name$="-hours"]');
var minInput = row.querySelector('input[name$="-minutes"]');
var total = (parseInt(hoursInput.value || 0, 10) * 60) +
parseInt(minInput.value || 0, 10);
minutesInput.value = total || '';
}

document.querySelectorAll('.avail-day:not(.exam-day)').forEach(
function (btn) {
btn.addEventListener('click', function () {
selectDay(btn);
});
}
);

saveBtn.addEventListener('click', function () {
if (!currentDay) return;
var total = parseInt(minutesInput.value || 0, 10);
var row = document.querySelector(
'[data-formset-row="' + currentDay.dataset.index + '"]'
);
row.querySelector('input[name$="-hours"]').value = Math.floor(total / 60);
row.querySelector('input[name$="-minutes"]').value = total % 60;
currentDay.classList.toggle('has-value', total > 0);
});
})();
</script>
{% endblock %}
Loading