Skip to content

Commit 1bfc3f1

Browse files
committed
feature: v0.1 기초 완성
1 parent 8606c95 commit 1bfc3f1

File tree

14 files changed

+423
-19
lines changed

14 files changed

+423
-19
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Velog Dashboard Project
22

3-
> 가나다라
3+
> velog dashboard
44
55
## Getting Started
66

backend/src/models/postStats.js

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@ PostStatsSchema.statics.aggTotalByUserId = async function (userId) {
3333
// 3. 해당 모델들의 stats 의 오늘 viewCount 값을 모두 더한 값
3434
// 4. 해당 모델들의 stats 의 마지막 요소의 likeCount 값을 모두 더한 값
3535

36+
// 오늘 날짜 설정
37+
// UTC 기준으로 오늘 날짜 설정
38+
const KST_OFFSET = 9 * 60 * 60 * 1000; // 한국 시간대는 UTC+9
39+
const now = new Date();
40+
let today = new Date(now.getTime() + KST_OFFSET); // UTC 시간에 한국 시간대를 적용
41+
42+
// 다시 문자열로 년월일 문자열로 바꾸고, 다시 문자열을 date object로 바꾸고
43+
// 시 분 초 0으로, UTC 시간대로 만들기
44+
const dateString = today.toISOString().split("T")[0];
45+
today = new Date(dateString);
46+
3647
try {
3748
const aggreateResult = await this.aggregate([
3849
// userId에 맞는 문서를 필터링
@@ -42,17 +53,33 @@ PostStatsSchema.statics.aggTotalByUserId = async function (userId) {
4253
{
4354
$project: {
4455
totalViewCount: 1, // totalViewCount 값을 포함
45-
lastViewCount: { $last: "$stats.viewCount" }, // stats 배열의 마지막 viewCount 값
56+
todayViews: {
57+
// 오늘 날짜에 해당하는 stats 요소 필터링
58+
$filter: {
59+
input: "$stats",
60+
as: "stat",
61+
cond: { $eq: ["$$stat.date", today] }
62+
}
63+
},
4664
lastLikeCount: { $last: "$stats.likeCount" } // stats 배열의 마지막 likeCount 값
4765
}
4866
},
4967

50-
// 모든 문서에 대해 totalViewCount와 lastLikeCount를 더함
68+
// 오늘 날짜의 viewCount를 합산
69+
{
70+
$project: {
71+
totalViewCount: 1,
72+
todayViewCount: { $sum: "$todayViews.viewCount" },
73+
lastLikeCount: 1
74+
}
75+
},
76+
77+
// 모든 문서에 대해 totalViewCount, todayViewCount, lastLikeCount를 더함
5178
{
5279
$group: {
5380
_id: null,
5481
totalViewCountSum: { $sum: "$totalViewCount" },
55-
todayViewCountSum: { $sum: "$lastViewCount" },
82+
todayViewCountSum: { $sum: "$todayViewCount" },
5683
totalLastLikeCountSum: { $sum: "$lastLikeCount" }
5784
}
5885
}
@@ -83,7 +110,6 @@ PostStatsSchema.statics.allPostsAggByUserId = async function (userId) {
83110
};
84111
}
85112

86-
87113
const lastStat = doc.stats[doc.stats.length - 1];
88114
const secondLastStat = doc.stats[doc.stats.length - 2];
89115

backend/src/routes/postStatsRouter.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
"use strict";
22
import { userAuth } from "../middlewares/auth.js";
3-
import { totalStats, getAllPostsWithTotal } from "../services/postStatsServices.js";
3+
import { totalStats, getAllPostsWithTotal, getPostByUUID } from "../services/postStatsServices.js";
44

55
// ==================== Routing ==================== //
66

77
const postStatsRouter = (app, endpoint) => {
8+
app.route(`${endpoint}`).get(userAuth, getPostByUUID);
89
app.route(`${endpoint}/:userId`).get(userAuth, getAllPostsWithTotal);
910
app.route(`${endpoint}/total/:userId`).get(userAuth, totalStats);
1011
};

backend/src/services/postStatsServices.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export const totalStats = async (req, res) => {
77
try {
88
const { params: { userId } } = req;
99
if (req.user.userId != userId) {
10-
return res.status(404).json({ message: "Access Denied" });
10+
return res.status(403).json({ message: "Access Denied" });
1111
}
1212

1313
const result = await PostStats.aggTotalByUserId(userId);
@@ -26,7 +26,7 @@ export const getAllPostsWithTotal = async (req, res) => {
2626
try {
2727
const { params: { userId }, query: { sortBy, order } } = req;
2828
if (req.user.userId != userId) {
29-
return res.status(404).json({ message: "Access Denied" });
29+
return res.status(403).json({ message: "Access Denied" });
3030
}
3131

3232
// 정렬 함수를 정의
@@ -57,4 +57,26 @@ export const getAllPostsWithTotal = async (req, res) => {
5757
console.error(error);
5858
return res.status(500).json({ message: error.message });
5959
}
60-
}
60+
};
61+
62+
63+
export const getPostByUUID = async (req, res) => {
64+
try {
65+
const { query: { uuid } } = req;
66+
if (!uuid) {
67+
return res.status(404).json({ message: "UUID cannot be empty." });
68+
}
69+
70+
const post = await PostStats.findOne({ uuid }, { _id: 0, uuid: 0 });
71+
if (req.user.userId != post.userId) {
72+
return res.status(403).json({ message: "Access Denied" });
73+
}
74+
return res.status(200).json({
75+
message: "User's Total Posts with total",
76+
result: post
77+
});
78+
} catch (error) {
79+
console.error(error);
80+
return res.status(500).json({ message: error.message });
81+
}
82+
};

nginx/pages/dashboard/index.css

Lines changed: 114 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ body {
99
color: white;
1010
}
1111

12+
/* ====================================================== */
13+
/* 네비게이션바 관련 CSS */
14+
/* ====================================================== */
15+
1216
.sidebar {
1317
background-color: #222;
1418
width: 200px;
@@ -44,6 +48,10 @@ body {
4448
background: var(--main-velog-color);
4549
}
4650

51+
/* ====================================================== */
52+
/* 메인과 메인섹션 & Block 관련 CSS */
53+
/* ====================================================== */
54+
4755
.main-content {
4856
transition: margin-left 0.3s ease;
4957
}
@@ -119,7 +127,7 @@ nav ul li:last-child a {
119127
color: var(--main-velog-color);
120128
}
121129

122-
.stats-section-block-detail div {
130+
.stats-section-block-detail>div {
123131
display: flex;
124132
flex-direction: row;
125133
font-size: 0.85rem;
@@ -163,6 +171,10 @@ nav ul li:last-child a {
163171
color: var(--main-velog-color);
164172
}
165173

174+
/* ====================================================== */
175+
/* 동적으로 랜더링되는 post list 관련 CSS */
176+
/* ====================================================== */
177+
166178
#posts-list {
167179
width: 100%;
168180
}
@@ -178,6 +190,30 @@ nav ul li:last-child a {
178190
border-radius: 15px;
179191
}
180192

193+
.posts-list-graph {
194+
padding: 10px;
195+
cursor: pointer;
196+
font-size: 0.8rem;
197+
font-weight: bolder;
198+
color: var(--main-velog-color);
199+
}
200+
201+
.posts-list-graph:hover {
202+
background-color: #444;
203+
}
204+
205+
.posts-list-graph::after {
206+
content: "그래프 ▼";
207+
}
208+
209+
.graph-active::after {
210+
content: "그래프 ▲";
211+
padding: 6px;
212+
border-radius: 15px;
213+
background-color: var(--main-velog-color);
214+
color: white;
215+
}
216+
181217
.posts-list-today-veiw {
182218
display: flex;
183219
align-items: center;
@@ -188,6 +224,68 @@ nav ul li:last-child a {
188224
margin-right: 5px;
189225
}
190226

227+
/* ====================================================== */
228+
/* 동적으로 랜더링되는 차트 관련 CSS */
229+
/* ====================================================== */
230+
.post-graph-div-date {
231+
display: flex;
232+
align-items: center;
233+
flex-direction: row;
234+
justify-content: center;
235+
}
236+
237+
/* 모든 버튼과 링크에 대한 스타일링 */
238+
.post-graph-div-btn {
239+
background-color: var(--main-velog-color);
240+
color: white;
241+
padding: 7px;
242+
text-decoration: none;
243+
border: none;
244+
border-radius: 4px;
245+
cursor: pointer;
246+
}
247+
248+
/* 버튼과 링크에 대한 호버 효과 */
249+
.post-graph-div-btn:hover {
250+
background-color: #666;
251+
color: var(--main-velog-color);
252+
transition: background-color 0.3s, color 0.3s;
253+
}
254+
255+
256+
/* 날짜 선택기 스타일링 */
257+
.post-graph-div-date input[type="date"] {
258+
background-color: #333;
259+
color: #ccc;
260+
border: 1px solid #444;
261+
padding: 5px;
262+
border-radius: 4px;
263+
margin: 5px;
264+
font-size: 0.8rem;
265+
}
266+
267+
/* 입력 필드에 대한 호버 및 포커스 효과 */
268+
.post-graph-div-date input[type="date"]:hover,
269+
.post-graph-div-date input[type="date"]:focus {
270+
border-color: var(--main-velog-color);
271+
/* 호버 및 포커스시 테두리 색상 */
272+
outline: none;
273+
/* 기본 아웃라인 제거 */
274+
}
275+
276+
/* 레이블 스타일링 */
277+
.post-graph-div-date label {
278+
font-size: 0.8rem;
279+
color: #ccc;
280+
margin-bottom: 5px;
281+
}
282+
283+
284+
/* ====================================================== */
285+
/* 그 외 & footer 관련 CSS */
286+
/* ====================================================== */
287+
288+
191289
hr {
192290
border: 1px solid #444;
193291
}
@@ -200,23 +298,34 @@ hr {
200298
}
201299

202300

203-
204-
/* ==================================================== */
301+
/* ====================================================== */
205302
/* MOBILE */
206-
/* ==================================================== */
303+
/* ====================================================== */
207304

208305
@media only screen and (max-width: 600px) {
209306

210307
/* 화면 너비가 600px 이하일 때 적용할 스타일을 여기에 작성합니다. */
211308
.stats-section {
212309
flex-direction: column;
213310
}
311+
312+
.posts-list-graph::after {
313+
content: "▼";
314+
}
315+
316+
.graph-active::after {
317+
content: "▲";
318+
padding: 6px;
319+
border-radius: 15px;
320+
background-color: var(--main-velog-color);
321+
color: white;
322+
}
214323
}
215324

216325
@media only screen and (max-width: 1200px) {
217326

218327
/* 화면 너비가 1200px 이하일 때 적용할 스타일을 여기에 작성합니다. */
219328
#posts-list li a {
220-
max-width: 740px;
329+
max-width: 650px;
221330
}
222331
}

nginx/pages/dashboard/index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
/>
1313
<!-- Sweetalert2 -->
1414
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
15+
<!-- ChartJS, date adpater -->
16+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
17+
<script src="https://cdn.jsdelivr.net/npm/luxon@^2.0.0"></script>
18+
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@^1.0.0"></script>
19+
1520
<!-- CUSTOM -->
1621
<link rel="stylesheet" href="./index.css" />
1722
</head>

0 commit comments

Comments
 (0)