Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into feat/#28
Browse files Browse the repository at this point in the history
  • Loading branch information
lkhoony committed Aug 25, 2024
2 parents 0425693 + e183f53 commit 10cf054
Show file tree
Hide file tree
Showing 17 changed files with 430 additions and 108 deletions.
13 changes: 9 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@ jobs:
run: yarn install

- name: Generate build
id: build
env:
VITE_API_BASE_URL: ${{ secrets.VITE_API_BASE_URL }}
VITE_OAUTH_KAKAO_REST_API_KEY: ${{ secrets.VITE_OAUTH_KAKAO_REST_API_KEY }}
VITE_OAUTH_KAKAO_CLIENT_SECRET_CODE: ${{ secrets.VITE_OAUTH_KAKAO_CLIENT_SECRET_CODE }}
VITE_OAUTH_KAKAO_REDIRECT_URI: ${{ secrets.VITE_OAUTH_KAKAO_REDIRECT_URI }}
run: yarn build
continue-on-error: true

- name: Deploy to S3
id: deploy
if: steps.build.outcome == 'success'
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Expand All @@ -54,20 +57,22 @@ jobs:
작성자: ${{ steps.get_commit_info.outputs.author }}
- name: Discord notification - Failure
if: steps.deploy.outcome == 'failure'
if: steps.build.outcome == 'failure' || steps.deploy.outcome == 'failure'
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_DEPLOY_WEBHOOK }}
DISCORD_USERNAME: GitHub
DISCORD_AVATAR: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
uses: Ilshidur/action-discord@master
with:
args: |
❌ 배포 중 오류가 발생했습니다.
${{ steps.build.outcome == 'failure' && '빌드 중' || '배포 중' }} 오류가 발생했습니다.
브랜치: develop
커밋: ${{ steps.get_commit_info.outputs.message }}
작성자: <@${{ steps.get_commit_info.outputs.author_username }}>
작성자: <@${{ secrets.DISCORD_ID_1 }}>
실패한 워크플로우: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
${{ steps.build.outcome == 'failure' && '빌드 오류 메시지:' || '' }}
${{ steps.build.outcome == 'failure' && steps.build.outputs.stderr || '' }}
- name: Check deploy result
if: steps.deploy.outcome == 'failure'
if: steps.build.outcome == 'failure' || steps.deploy.outcome == 'failure'
run: exit 1
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.8.1",
"socket.io-client": "^4.7.5",
"zustand": "^4.5.5"
},
"devDependencies": {
Expand Down
4 changes: 4 additions & 0 deletions src/assets/icons/crew-panel-close-button.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions src/assets/icons/good-posture-check-button-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/assets/icons/group-side-nav-button.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 0 additions & 20 deletions src/assets/icons/more-icon.svg

This file was deleted.

10 changes: 10 additions & 0 deletions src/assets/icons/posture-guide-button-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions src/assets/icons/question-info-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/ranking-guide.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 15 additions & 8 deletions src/components/PoseDetector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import GuidePopup from "./Posture/GuidePopup"
import { useSnapshotStore } from "@/store/SnapshotStore"
import { useCreateSnaphot } from "@/hooks/useSnapshotMutation"
import { position } from "@/api"
import PostureCheckIcon from "@assets/icons/good-posture-check-button-icon.svg?react"
import GuideIcon from "@assets/icons/posture-guide-button-icon.svg?react"

const PoseDetector: React.FC = () => {
const [isScriptLoaded, setIsScriptLoaded] = useState<boolean>(false)
const [isScriptError, setIsScriptError] = useState<boolean>(false)
const [slope, setSlope] = useState<string | null>(null)
const [isTextNeck, setIsTextNeck] = useState<boolean | null>(null)
const [isModelLoaded, setIsModelLoaded] = useState<boolean>(false)
const [mode] = useState<string>("snapshot")
const [isSnapSaved, setIsSnapSaved] = useState<boolean>(false)
const [isPopupVisible, setIsPopupVisible] = useState<boolean>(false)
const modelRef = useRef<any>(null)
Expand Down Expand Up @@ -81,7 +82,7 @@ const PoseDetector: React.FC = () => {
}
if (snapRef.current) {
const _slope = detectSlope(snapRef.current, results, false)
const _isTextNeck = detectTextNeck(snapRef.current, results, mode === "snapshot")
const _isTextNeck = detectTextNeck(snapRef.current, results, true)
if (_slope !== null) setSlope(_slope)
if (_isTextNeck !== null) setIsTextNeck(_isTextNeck)

Expand All @@ -104,7 +105,7 @@ const PoseDetector: React.FC = () => {
}
}
},
[mode, setSlope, setIsTextNeck, showNotification]
[setSlope, setIsTextNeck, showNotification]
)

const detectStart = useCallback(
Expand Down Expand Up @@ -208,18 +209,24 @@ const PoseDetector: React.FC = () => {
: "올바르지 않은 자세입니다."}
</div>
{!isSnapSaved && (
<div className="absolute bottom-0 flex w-full items-center justify-center gap-[20px] p-[50px] text-white">
<div className="absolute bottom-0 flex w-full items-center justify-center gap-[16px] p-[50px] text-white">
<button
className="rounded rounded-full bg-white bg-opacity-80 p-[20px] text-black"
className="flex w-[260px] items-center justify-center rounded rounded-full bg-white bg-opacity-80 p-[20px] text-black"
onClick={handleShowPopup}
>
가이드 다시 볼게요!
<div className="flex flex-row items-center gap-2">
<GuideIcon />
<span>가이드 다시 볼게요!</span>
</div>
</button>
<button
className="rounded rounded-full bg-[#1A75FF] bg-opacity-80 p-[20px] text-white"
className="flex w-[260px] items-center justify-center rounded rounded-full bg-[#1A75FF] bg-opacity-80 p-[20px] text-white"
onClick={getInitSnap}
>
바른자세를 취했어요!
<div className="flex flex-row items-center gap-2">
<PostureCheckIcon />
바른자세를 취했어요!
</div>
</button>
</div>
)}
Expand Down
141 changes: 141 additions & 0 deletions src/components/Posture/PostrueCrew.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import CloseCrewPanelIcon from "@assets/icons/crew-panel-close-button.svg?react"
import QuestionIcon from "@assets/icons/question-info-icon.svg?react"
import PostureGuide from "@assets/icons/posture-guide-button-icon.svg?react"
import RankingGuideToolTip from "@assets/images/ranking-guide.png"
import { useEffect, useState } from "react"
import SelectBox from "@components/SelectBox"

interface IPostureCrew {
groupUserId: number
uid: number
nickname: string
rank: number
score: number
}

interface PostureCrewProps {
toggleSidebar: () => void
}

export default function PostrueCrew(props: PostureCrewProps) {
const { toggleSidebar } = props
const [crews, setCrews] = useState<IPostureCrew[]>([])
const [isConnected, setIsConnected] = useState<"loading" | "success" | "disconnected">("loading")
const [isEnabled, setIsEnabled] = useState(true)
const [notiAlarmTime, setNotiAlarmTime] = useState("틀어진 즉시")

useEffect(() => {
const token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJoZXJvLWFsaWdubGFiLWFwaSIsImF1ZCI6Imhlcm8tYWxpZ25sYWItYXBpIiwiaWQiOjIwMDAwMSwidHlwZSI6ImFjY2Vzc1Rva2VuIiwiZXhwIjoxNzM1Mzk4MDAwfQ.pIl87yrMX4EVoLlBOG0A2X5AMRRUXalwMKnfH6cSDE8"
const socket = new WebSocket(`wss://api.alignlab.site/ws/v1/groups/1/users?X-HERO-AUTH-TOKEN=${token}`)

socket.onopen = () => {
console.log("WebSocket connected")
setIsConnected("success")
}

socket.onmessage = (event) => {
const data = JSON.parse(event.data)
setCrews(data.groupUsers || [])
}

socket.onerror = (error) => {
console.error("WebSocket error:", error)
}

socket.onclose = (event) => {
console.log("WebSocket disconnected. Code:", event.code, "Reason:", event.reason)
setIsConnected("disconnected")
}

return () => {
socket.close()
}
}, [])

const onClickCloseSideNavButton = () => {
toggleSidebar()
}

const onClickNotiAlarmTime = (value: string) => {
setNotiAlarmTime(value)
}

return (
<div className="flex h-full flex-col rounded-lg bg-[#FAFAFA] p-4">
<button onClick={onClickCloseSideNavButton} className="mb-8 p-1">
<CloseCrewPanelIcon />
</button>
<div className="flex-grow">
<div className="flex items-center justify-between p-2 pb-3">
<div className="flex items-center">
<span className="font-medium">자세 알림</span>
</div>

<label className="relative inline-flex cursor-pointer items-center">
<input
type="checkbox"
className="peer sr-only"
checked={isEnabled}
onChange={() => setIsEnabled(!isEnabled)}
/>
<div className="peer h-6 w-11 rounded-full bg-gray-200 after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:border after:border-gray-300 after:bg-white after:transition-all after:content-[''] peer-checked:bg-blue-600 peer-checked:after:translate-x-full peer-checked:after:border-white"></div>
</label>
</div>

<div className="pb-8 pl-2 pr-2">
<SelectBox
options={["틀어진 즉시", "15분 간격", "30분 간격", "45분 간격", "1시간 간격"]}
value={notiAlarmTime}
onClick={onClickNotiAlarmTime}
/>
</div>

<div className="group relative">
<div className="flex items-center gap-2 p-2">
<span>자세 랭킹</span>
<QuestionIcon />
</div>

<div className="invisible absolute left-4 top-6 z-10 h-full w-full opacity-0 transition-all duration-300 group-hover:visible group-hover:opacity-100">
<img src={RankingGuideToolTip} alt="랭킹 가이드" />
</div>
</div>
<div>
{isConnected === "loading" && <p>서버와 연결 중입니다.</p>}
{isConnected === "disconnected" && <p>서버와 연결 끊어졌습니다.</p>}
{isConnected === "success" && crews.length === 0 && (
<p className="text-center text-gray-500">접속자가 없습니다.</p>
)}
{isConnected === "success" && crews.length > 0 && (
<ul className="space-y-2">
{crews.map((user, index) => (
<li key={index} className="flex h-14 w-[200px] items-center justify-between rounded-full bg-white">
<div className="flex w-full items-center justify-between">
<div>
<span
className={`ml-6 mr-4 text-center font-semibold ${
user.rank <= 3 ? "text-[#1F76F8]" : "text-[#9D9DA2]"
}`}
>
{user.rank}
</span>
<span className="font-semibold text-[#202124]">{user.nickname}</span>
</div>
<span className="text-normal mr-6 text-[13px] text-[#999]">{user.score}</span>
</div>
</li>
))}
</ul>
)}
</div>
</div>
<div className="mt-auto pb-0.5 pl-0.5">
<div className="flex cursor-pointer items-center gap-3">
<PostureGuide />
<span>바른자세 가이드</span>
</div>
</div>
</div>
)
}
52 changes: 52 additions & 0 deletions src/components/SelectBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useState } from "react"

interface SelectBoxProps {
value: string
options: string[]
onClick: (selectedValue: string) => void
}

export default function SelectBox(props: SelectBoxProps) {
const { value, options, onClick } = props
const [isOpen, setIsOpen] = useState(false)

const toggleDropdown = () => setIsOpen(!isOpen)

const handleOptionClick = (option: string) => {
onClick(option)
setIsOpen(false)
}

return (
<div className="relative w-full">
<div
className="flex h-[40px] w-full cursor-pointer items-center justify-between rounded-md border border-[#E5E8EB] bg-white px-3 py-2.5"
onClick={toggleDropdown}
>
<span>{value}</span>
<svg
className={`h-4 w-4 transform transition-transform ${isOpen ? "rotate-180" : ""}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</div>
{isOpen && (
<div className="absolute z-10 mt-1 w-full flex-col rounded-md bg-white py-1 shadow-[0px_2px_16px_0px_rgba(0,0,0,0.13)]">
{options.map((option) => (
<div
key={option}
className="cursor-pointer px-3 py-2 hover:bg-gray-100"
onClick={() => handleOptionClick(option)}
>
{option}
</div>
))}
</div>
)}
</div>
)
}
6 changes: 1 addition & 5 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,4 @@ import ReactDOM from "react-dom/client"
import App from "./App"
import "@/style/tailwind.css"

ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
ReactDOM.createRoot(document.getElementById("root")!).render(<App />)
Loading

0 comments on commit 10cf054

Please sign in to comment.