Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"use client"

import { Chip, Stack, Typography, Box } from "@mui/material"
import { User } from "@server/entity/user"
import CheckCircleIcon from "@mui/icons-material/CheckCircle"
import CancelIcon from "@mui/icons-material/Cancel"
import SchoolIcon from "@mui/icons-material/School"
import WorkIcon from "@mui/icons-material/Work"
import AccessTimeIcon from "@mui/icons-material/AccessTime"
import EventAvailableIcon from "@mui/icons-material/EventAvailable"

interface RetreatAttendanceCardProps {
soon: User
}

export default function RetreatAttendanceCard({
soon,
}: RetreatAttendanceCardProps) {
const isRegistered = !!soon.retreatAttend && !soon.retreatAttend.isCanceled

return (
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
padding="16px 20px"
borderRadius="16px"
bgcolor="white"
boxShadow="0 4px 20px 0 rgba(0,0,0,0.05)"
border={isRegistered ? "1px solid #4caf50" : "1px solid #eee"}
>
<Stack gap="4px">
<Stack direction="row" alignItems="center" gap="8px">
<Typography fontWeight="700" fontSize="18px">
{soon.name}
</Typography>
<Typography fontSize="14px" color="text.secondary">
{soon.yearOfBirth}
</Typography>
</Stack>

{isRegistered ? (
<Stack direction="row" gap="6px" mt="4px">
<Chip
label={soon.retreatAttend?.isHalf ? "부분참석" : "전체참석"}
size="small"
variant="outlined"
color={soon.retreatAttend?.isHalf ? "info" : "success"}
icon={
soon.retreatAttend?.isHalf ? (
<AccessTimeIcon />
) : (
<EventAvailableIcon />
)
}
sx={{ borderRadius: "6px" }}
/>
<Chip
label={soon.retreatAttend?.isWorker ? "직장인" : "학생"}
size="small"
variant="outlined"
color={soon.retreatAttend?.isWorker ? "secondary" : "primary"}
icon={
soon.retreatAttend?.isWorker ? <WorkIcon /> : <SchoolIcon />
}
sx={{ borderRadius: "6px" }}
/>
</Stack>
) : (
<Typography fontSize="13px" color="#ff6b6b" fontWeight="500">
수련회 미신청
</Typography>
)}
</Stack>

<Box>
{isRegistered ? (
<Stack alignItems="center" gap="2px">
<CheckCircleIcon sx={{ color: "#4caf50", fontSize: 28 }} />
<Typography fontSize="10px" fontWeight="600" color="#4caf50">
신청완료
</Typography>
</Stack>
) : (
<Stack alignItems="center" gap="2px">
<CancelIcon sx={{ color: "#e0e0e0", fontSize: 28 }} />
<Typography fontSize="10px" fontWeight="600" color="#bdbdbd">
미신청
</Typography>
</Stack>
)}
</Box>
</Stack>
)
}
37 changes: 37 additions & 0 deletions client/src/app/leader/retreat-attendance-for-same-age/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use client"

import axios from "@/config/axios"
import { Stack } from "@mui/material"
import { User } from "@server/entity/user"
import { useEffect, useState } from "react"
import RetreatAttendanceCard from "./RetreatAttendanceCard"

export default function RetreatAttendancePage() {
const [soonList, setSoonList] = useState<User[]>([])
useEffect(() => {
fetchSoonList()
}, [])

async function fetchSoonList() {
try {
const response = await axios.get<User[]>(
"/retreat/retreat-attendance-records-for-same-age",
)
setSoonList(response.data)
} catch (error) {
console.error("Failed to fetch soon list:", error)
}
}

return (
<Stack>
<Stack gap="8px" padding="16px">
{soonList.map((soon) => (
<Stack key={soon.id}>
<RetreatAttendanceCard soon={soon} />
</Stack>
))}
</Stack>
</Stack>
)
}
2 changes: 1 addition & 1 deletion client/src/app/leader/retreat-attendance/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default function RetreatAttendancePage() {
async function fetchSoonList() {
try {
const response = await axios.get<User[]>(
"/soon/retreat-attendance-records"
"/soon/retreat-attendance-records",
)
setSoonList(response.data)
} catch (error) {
Expand Down
7 changes: 7 additions & 0 deletions client/src/components/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ export default function Header() {
type: "menu",
})

DrawerItems.push({
title: "동기 수련회 접수 조회",
icon: <HowToRegIcon fontSize="small" sx={{ color: "#667eea" }} />,
path: "/leader/retreat-attendance-for-same-age",
type: "menu",
})

if (authUserData?.role.VillageLeader) {
DrawerItems.push({
title: "전체 출석 조회",
Expand Down
39 changes: 38 additions & 1 deletion server/src/routes/retreat/retreatRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
retreatAttendDatabase,
userDatabase,
} from "../../model/dataSource"
import { getUserFromToken } from "../../util/util"
import { checkJwt, getUserFromToken } from "../../util/util"
import adminRouter from "./adminRouter"
import sharingRouter from "./sharingRouter"
import { getKakaoIdFromAccessToken } from "../../util/auth"
Expand Down Expand Up @@ -186,6 +186,43 @@ router.get("/isRegistered", async (req, res) => {
res.send({ result: foundUser ? "success" : "fail" })
})

router.get("/retreat-attendance-records-for-same-age", async (req, res) => {
const user = await checkJwt(req)
if (!user) {
res.status(401).send({ error: "Unauthorized" })
return
}
if (!user.role.Leader) {
res.status(403).send({ error: "Forbidden" })
return
}

let attendDataList = await userDatabase.find({
where: {
yearOfBirth: user.yearOfBirth,
},
select: {
id: true,
name: true,
yearOfBirth: true,
gender: true,
phone: true,
retreatAttend: {
id: true,
isWorker: true,
isHalf: true,
createAt: true,
isCanceled: true,
},
},
relations: {
retreatAttend: true,
},
})

res.status(200).send(attendDataList)
})

router.use("/sharing", sharingRouter)
router.use("/admin", adminRouter)

Expand Down
Loading