Showing
7 changed files
with
218 additions
and
0 deletions
views/auction.pug
0 → 100644
1 | +extends layout | ||
2 | + | ||
3 | +block good | ||
4 | + h2= good.name | ||
5 | + div= '등록자: ' + good.owner.nick | ||
6 | + div= '시작가: ' + good.price + '원' | ||
7 | + strong#time(data-start=good.createdAt) | ||
8 | + img#good-img(src='/img/' + good.img) | ||
9 | + | ||
10 | +block content | ||
11 | + .timeline | ||
12 | + #bid | ||
13 | + for bid in auction | ||
14 | + div | ||
15 | + span= bid.user.nick + '님: ' | ||
16 | + strong= bid.bid + '원에 입찰하셨습니다.' | ||
17 | + if bid.msg | ||
18 | + span= '(' + bid.msg + ')' | ||
19 | + form#bid-form | ||
20 | + input(type='number' name='bid' placeholder='입찰가' required min=good.price) | ||
21 | + input(type='msg' name='msg' placeholder='메시지(선택사항)' maxlength='100') | ||
22 | + button.btn(type='submit') 입찰 | ||
23 | + if auctionError | ||
24 | + .error-message= auctionError | ||
25 | + script(src='https://cdnjs.cloudflare.com/ajax/libs/event-source-polyfill/0.0.9/eventsource.min.js') | ||
26 | + script(src='/socket.io/socket.io.js') | ||
27 | + script. | ||
28 | + document.querySelector('#bid-form').addEventListener('submit', function (e) { | ||
29 | + e.preventDefault(); | ||
30 | + var xhr = new XMLHttpRequest(); | ||
31 | + var errorMessage = document.querySelector('.error-message'); | ||
32 | + xhr.onload = function () { | ||
33 | + if (xhr.status === 200) { | ||
34 | + e.target.bid.value = ''; | ||
35 | + e.target.msg.value = ''; | ||
36 | + errorMessage.textContent = ''; | ||
37 | + } else { | ||
38 | + console.error(xhr.responseText); | ||
39 | + e.target.bid.value = ''; | ||
40 | + e.target.msg.value = ''; | ||
41 | + errorMessage.textContent = xhr.responseText | ||
42 | + } | ||
43 | + }; | ||
44 | + xhr.open('POST', '/good/#{good.id}/bid'); | ||
45 | + xhr.setRequestHeader('Content-Type', 'application/json'); | ||
46 | + xhr.send(JSON.stringify({ | ||
47 | + bid: e.target.bid.value, | ||
48 | + msg: e.target.msg.value, | ||
49 | + })); | ||
50 | + }); | ||
51 | + var es = new EventSource("/sse"); | ||
52 | + var time = document.querySelector('#time'); | ||
53 | + es.onmessage = function (e) { | ||
54 | + var end = new Date(time.dataset.start); | ||
55 | + var server = new Date(parseInt(e.data, 10)); | ||
56 | + end.setDate(end.getDate() + 1); | ||
57 | + if (server >= end) { | ||
58 | + return time.textContent = '00:00:00'; | ||
59 | + } else { | ||
60 | + var t = end - server; | ||
61 | + var seconds = ('0' + Math.floor((t / 1000) % 60)).slice(-2); | ||
62 | + var minutes = ('0' + Math.floor((t / 1000 / 60) % 60)).slice(-2); | ||
63 | + var hours = ('0' + Math.floor((t / (1000 * 60 * 60)) % 24)).slice(-2); | ||
64 | + return time.textContent = hours + ':' + minutes + ':' + seconds; | ||
65 | + } | ||
66 | + }; | ||
67 | + var socket = io.connect('http://localhost:8010', { | ||
68 | + path: '/socket.io' | ||
69 | + }); | ||
70 | + socket.on('bid', function (data) { | ||
71 | + var div = document.createElement('div'); | ||
72 | + var span = document.createElement('span'); | ||
73 | + span.textContent = data.nick + '님: '; | ||
74 | + var strong = document.createElement('strong'); | ||
75 | + strong.textContent = data.bid + '원에 입찰하셨습니다.'; | ||
76 | + div.appendChild(span); | ||
77 | + div.appendChild(strong); | ||
78 | + if (data.msg) { | ||
79 | + span = document.createElement('span'); | ||
80 | + span.textContent = '(' + data.msg + ')'; | ||
81 | + div.appendChild(span); | ||
82 | + } | ||
83 | + document.querySelector('#bid').appendChild(div); | ||
84 | + }); |
views/error.pug
0 → 100644
views/good.pug
0 → 100644
1 | +extends layout | ||
2 | + | ||
3 | +block content | ||
4 | + .timeline | ||
5 | + form#good-form(action='/good' method='post' enctype='multipart/form-data') | ||
6 | + .input-group | ||
7 | + label(for='good-name') 상품명 | ||
8 | + input#good-name(name='name' required autofocus style='height:50px;') | ||
9 | + .input-group | ||
10 | + label(for='good-photo') 상품 사진 | ||
11 | + input#good-photo(type='file' name='img' required autofocus style='height:50px;') | ||
12 | + .input-group | ||
13 | + label(for='good-price') 시작 가격 | ||
14 | + input#good-price(type='number' name='price' required style='height:50px;') | ||
15 | + button#join-btn.btn(type='submit' mar style="font-family:Yeon Sung ") 상품 등록 |
views/join.pug
0 → 100644
1 | +extends layout | ||
2 | + | ||
3 | +block content | ||
4 | + .timeline | ||
5 | + form#join-form(action='/auth/join' method='post') | ||
6 | + .input-group | ||
7 | + label(for='join-email') 이메일 | ||
8 | + input#join-email(type='email' name='email') | ||
9 | + .input-group | ||
10 | + label(for='join-nick') 닉네임 | ||
11 | + input#join-nick(type='text' name='nick') | ||
12 | + .input-group | ||
13 | + label(for='join-password') 비밀번호 | ||
14 | + input#join-password(type='password' name='password') | ||
15 | + .input-group | ||
16 | + label(for='join-money') 보유자산 | ||
17 | + input#join-money(type='number' name='money') | ||
18 | + if joinError | ||
19 | + .error-message= joinError | ||
20 | + button#join-btn.btn(type='submit' style='font-family:Yeon Sung') 회원가입 |
views/layout.pug
0 → 100644
1 | +doctype | ||
2 | +html | ||
3 | + head | ||
4 | + meta(charset='UTF-8') | ||
5 | + title= title | ||
6 | + meta(name='viewport' content='width=device-width, user-scalable=no') | ||
7 | + meta(http-equiv='X-UA-Compatible' content='IE=edge') | ||
8 | + link(rel='stylesheet' href='/main.css') | ||
9 | + | ||
10 | + link(rel='stylesheet' href='https://fonts.googleapis.com/css?family=Yeon+Sung&subset=korean') | ||
11 | + link(rel='stylesheet' href='https://fonts.googleapis.com/css?family=Song+Myung&subset=korean') | ||
12 | + h1(style='font-family:Song Myung; font-size:50px; text-align:center; padding-top:30px;') | ||
13 | + | 중고로운 경희나라 | ||
14 | + body | ||
15 | + .container | ||
16 | + .profile-wrap | ||
17 | + .profile | ||
18 | + if user && user.id | ||
19 | + .user-name= '안녕하세요! ' + user.nick + '님' | ||
20 | + .user-money= '보유 자산: ' + user.money + '원' | ||
21 | + input#my-id(type='hidden' value=user.id) | ||
22 | + a#logout.btn(href='/auth/logout') 로그아웃 | ||
23 | + a#register.btn(href='/good') 상품 등록 | ||
24 | + a#list.btn(href='/list') 낙찰 내역 | ||
25 | + else | ||
26 | + form#login-form(action='/auth/login' method='post') | ||
27 | + .input-group | ||
28 | + label(for='email' style='font-family:Yeon Sung; font-size:20px; color:black') 이메일 | ||
29 | + input#email(type='email' name='email' required autofocus style='font-family:Yeon Sung; font-size:15px') | ||
30 | + .input-group | ||
31 | + label(for='password' style='font-family:Yeon Sung; font-size:20px; color:black') 비밀번호 | ||
32 | + input#password(type='password' name='password' required style='font-family:Yeon Sung; font-size:15px') | ||
33 | + if loginError | ||
34 | + .error-message= loginError | ||
35 | + a#join.btn(href='/join' style='font-family:Yeon Sung; font-size:20px') 회원가입 | ||
36 | + button#login.btn(type='submit' style='font-family:Yeon Sung; font-size:20px') 로그인 | ||
37 | + footer(style='font-family:Yeon Sung') | ||
38 | + | Made by 이정민 서예진 김대욱 | ||
39 | + block good | ||
40 | + block content |
views/list.pug
0 → 100644
views/main.pug
0 → 100644
1 | +extends layout | ||
2 | + | ||
3 | +block content | ||
4 | + .timeline | ||
5 | + h2 경매 진행 목록 | ||
6 | + table#good-list | ||
7 | + tr | ||
8 | + th 상품명 | ||
9 | + th 이미지 | ||
10 | + th 시작 가격 | ||
11 | + th 종료 시간 | ||
12 | + th 입장 | ||
13 | + for good in goods | ||
14 | + tr | ||
15 | + td= good.name | ||
16 | + td: img(src='/img/' + good.img) | ||
17 | + td= good.price | ||
18 | + td.time(data-start=good.createdAt) 00:00:00 | ||
19 | + td: a.enter.btn(href='/good/' + good.id) 입장 | ||
20 | + script(src='https://cdnjs.cloudflare.com/ajax/libs/event-source-polyfill/0.0.9/eventsource.min.js') | ||
21 | + script. | ||
22 | + var es = new EventSource('/sse'); | ||
23 | + es.onmessage = function (e) { | ||
24 | + document.querySelectorAll('.time').forEach(function(td) { | ||
25 | + var end = new Date(td.dataset.start); | ||
26 | + var server = new Date(parseInt(e.data, 10)); | ||
27 | + end.setDate(end.getDate() + 1); | ||
28 | + if (server >= end) { | ||
29 | + return td.textContent = '00:00:00'; | ||
30 | + } else { | ||
31 | + var t = end - server; | ||
32 | + var seconds = ('0' + Math.floor((t / 1000) % 60)).slice(-2); | ||
33 | + var minutes = ('0' + Math.floor((t / 1000 / 60) % 60)).slice(-2); | ||
34 | + var hours = ('0' + Math.floor((t / (1000 * 60 * 60)) % 24)).slice(-2); | ||
35 | + return td.textContent = hours + ':' + minutes + ':' + seconds ; | ||
36 | + } | ||
37 | + }); | ||
38 | + }; |
-
Please register or login to post a comment